ОГЛАВЛЕНИЕ

ВВЕДЕНИЕ. ЧТО ТАКОЕ СЕРВЕР ПРИЛОЖЕНИЙ "СМАРАГД"?

ПРЕИМУЩЕСТВА ТРЕХЗВЕННОЙ АРХИТЕКТУРЫ
ВОЗМОЖНОСТИ СЕРВЕРА ПРИЛОЖЕНИЙ "СМАРАГД"

1. ОБЗОР СЕРВЕРА ПРИЛОЖЕНИЙ "СМАРАГД"

1.1 АРХИТЕКТУРА СЕРВЕРА
1.2 ОБРАБОТКА ЗАПРОСОВ КЛИЕНТОВ
1.3 УПРАВЛЕНИЕ РАБОТОЙ СЕРВЕРА
1.4 ЖУРНАЛИЗАЦИЯ РАБОТЫ СЕРВЕРА

2. ИНТЕРФЕЙС СЕРВЕРА ПРИЛОЖЕНИЙ

3. ХРАНИМЫЕ ПРОЦЕДУРЫ СЕРВЕРА ПРИЛОЖЕНИЙ

3.1 ВНУТРЕННИЕ ХРАНИМЫЕ ПРОЦЕДУРЫ
3.2 ВНЕШНИЕ ХРАНИМЫЕ ПРОЦЕДУРЫ

4. ЯЗЫК ХРАНИМЫХ ПРОЦЕДУР СЕРВЕРА ПРИЛОЖЕНИЙ

4.1 НАИБОЛЕЕ ВАЖНЫЕ ОТЛИЧИЯ ОТ SPL
4.2 ЭЛЕМЕНТЫ ЯЗЫКА
4.3 СОЗДАНИЕ ХРАНИМЫХ ПРОЦЕДУР
4.4 ОПЕРАТОРЫ
4.5 ПРЕДОПРЕДЕЛЕННЫЕ ПСЕВДОПЕРЕМЕННЫЕ

Приложение 1.

Особенности взаимодействия КЛИЕНТОВ с сервером приложений в FoxPro 2.6.

Приложение 2.

ФУНКЦИИ СТАНДАРТНОЙ БИБЛИОТЕКИ

Приложение 3.

Синтаксис операторов языка ASPL

Приложение 4.

ПРИМЕР ИСПОЛЬЗОВАНИЯ СЕРВЕРА ПРИЛОЖЕНИЙ

ПОСТАНОВКА ЗАДАЧИ
ОПИСАНИЕ ВЗАИМОДЕЙСТВИЯ
ОПИСАНИЕ ПРОГРАММ. ФУНКЦИИ СЕРВЕРА
ОПИСАНИЕ ПРОГРАММ. ПРОГРАММЫ КЛИЕНТА
ОПИСАНИЕ ПРОГРАММ. ПРОГРАММА АДМИНИСТРАТОРА

Список литературы

ВВЕДЕНИЕ. ЧТО ТАКОЕ СЕРВЕР ПРИЛОЖЕНИЙ "СМАРАГД"?

Сервер приложений (СП) "СМАРАГД" - это набор программного обеспечения, позволяющий распределить обработку данных по сети, организовать специально выделенные серверы для выполнения определенных задач, многозадачный режим выполнения программ пользователя *).

ПРЕИМУЩЕСТВА ТРЕХЗВЕННОЙ АРХИТЕКТУРЫ

В традиционных архитектурах клиент/сервер (двухзвенных архитектурах) взаимодействие клиентской программы и сервера баз данных происходит напрямую. При этом вся логика обработки данных делится между клиентскими программами и серверами баз данных. На серверах баз данных производится первичная обработка данных с помощью механизма хранимых процедур, а их вторичная обработка данных выполняется на клиентском рабочем месте. При этом подходе при изменении структуры базы данных, сервера базы данных, порядка выполнения определенных операций над данными необходимо менять либо хранимые процедуры сервера, либо программы клиента. Первый вариант более предпочтителен, но все равно, для изменения процедуры, которой активно пользуются клиенты, необходимо произвести отключение пользователей от сервера. Одним из основных недостатков этого подхода является отсутствие возможности абстрагирования клиента от терминологии СУБД, от понятия СУБД, от конкретных серверов баз данных. Другим недостатком такого подхода является сильная нагрузка на клиентские программы из-за необходимости дополнительной обработки данных совместно с управлением интерфейсом с пользователем. Также при использовании двухзвенных архитектур возрастает "бесполезная" нагрузка на сеть, поскольку решение о том, нужны данные или нет, может быть принято при вторичной обработке на клиенте.

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

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

ВОЗМОЖНОСТИ СЕРВЕРА ПРИЛОЖЕНИЙ "СМАРАГД"

Сервер приложений "СМАРАГД" обладает следующими возможностями:

1. ОБЗОР СЕРВЕРА ПРИЛОЖЕНИЙ "СМАРАГД"

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

1.1 АРХИТЕКТУРА СЕРВЕРА

Сервер приложений "Смарагд" может поставляться в одном из двух вариантов: базовом и расширенном. В базовом варианте поставляется только ядро сервера приложений (описание ядра сервера приложений см. в разделе "Ядро сервера приложений"). В расширенном варианте вместе с ядром сервера поставляется внешний интерпретатор языка ASPL (описание языка ASPL см. в главе "Язык хранимых процедур").

ОБЩАЯ СХЕМА СЕРВЕРА

Общая схема сервера приложений показана на рис. 1.


Рис. 1. Схема сервера приложений

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

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

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

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

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

ЯДРО СЕРВЕРА ПРИЛОЖЕНИЙ

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

КЛИЕНТСКИЙ ПРОЦЕСС СЕРВЕРА

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

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

1.2 ОБРАБОТКА ЗАПРОСОВ КЛИЕНТОВ

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

На каждую команду клиента ядро сервера приложений высылает или подтверждение, или опровержение выполнения команды. Форматы этих ответов приведены в разд. "Интерфейс сервера приложений".

1.3 УПРАВЛЕНИЕ РАБОТОЙ СЕРВЕРА

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

Замечание. Администратор операционной системы (root в UNIX или Administrator в Windows NT) не имеет права исполнять эти команды, если он не является администратором сервера приложений. В случае посылки им этих команд, сервер приложений вернет ошибку.

1.4 ЖУРНАЛИЗАЦИЯ РАБОТЫ СЕРВЕРА

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

2. ИНТЕРФЕЙС СЕРВЕРА ПРИЛОЖЕНИЙ

Взаимодействие клиента и сервера приложений базируется на протоколе TCP/IP и организуется в виде обмена текстовыми строками (пакетами). Интерфейс клиента с сервером приложений является двухуровневым:

Для каждой команды клиента сервер приложений возвращает подтверждение о ее завершении, что является признаком окончания выполнения команды, или сообщение об ошибке. Сервер приложений поддерживает асинхронное выполнение команд клиента.

СТРУКТУРА КЛИЕНТСКОГО ПАКЕТА

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

Рис. 2. Общий формат пакета клиента.

Сигнатура пакета - стандартный 4-байтовый неизменяемый префикс пакета ("ASIF").

Длина пакета - беззнаковое длинное целое, показывающее размер тела пакета в байтах.

Тип пакета - однобайтовый символ, который принимает значение 'C' или 'D'.

Тело пакета - произвольная строка, содержащая команду или данные для сервера приложений (в общем виде - поток байт).

Пакеты клиента делятся на 2 вида:

  1. Пакеты с данными (тип пакета - 'D'), которые содержат информацию для клиентского процесса, работающего на сервере приложений.
  2. Командные пакеты (тип пакета - 'C') содержат команды администрирования и организации работы клиента с сервером приложений. Более подробно см. разд. "Язык команд сервера приложений".

ЯЗЫК КОМАНД СЕРВЕРА ПРИЛОЖЕНИЙ

Элементы языка - это команды, которые делятся на следующие классы:

LI <имя_клиента> <пароль>

BS

или

BS <имя запускаемого файла>

AS <идентификатор_сессии>

Рассмотрим примеры пакетов клиентов с использованием выше- перечисленных команд:

ASIF13CLI user1 pwd1 /* для команды LI, где

user1 - имя клиента,

pwd1 - пароль */

ASIF2CBS или ASIF12CBS file1.exe /* для команды BS, где

file1.exe - имя файла */

ASIF2CES /* для команды ES */

ASIF2CDS /* для команды DS */

ASIF7CAS id_s /* для команды AS, где

id_s - идентификатор сессии*/

ASIF2CLU /* для команды LU */

ASIF2CLT /* для команды LT */

СТРУКТУРА ОТВЕТНОГО ПАКЕТА

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

Рис. 3. Общий формат ответного пакета сервера приложений

Сигнатура ответного пакета - стандартный 4-байтовый неизменяемый префикс пакета ("ASIF").

Длина ответного пакета - беззнаковое длинное целое, показывающее размер тела ответного пакета в байтах.

Тип ответного пакета - однобайтовый символ, который принимает одно из следующих значений: 'D', 'R' или 'E'.

Тело ответного пакета - произвольная строка, содержащая ответ сервера приложений.

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

На каждую команду клиента сервер приложений высылает подтверждение о ее выполнении или сообщение об ошибке в случае невыполнения команды. Формат ответного пакета следующий:

LU <описание клиента>

:

LU END

LT <описание задачи>

:

LT END

Рассмотрим примеры ответных пакетов для указанных выше команд:

ASIF8RLI user1 /* для команды LI, где

user1 - имя клиента */

ASIF2RBS /* для команды BS */

ASIF2RES /* для команды ES */

ASIF7RDS id_s /* для команды DS, где

id_s - идентификатор сессии */

ASIF2RAS /* для команды AS */

ASIF8RLU BEGIN /* для команды LU, где

BEGIN указывает на начало описания

клиента */

ASIF12RLU user1 1 2 /* user1 - имя клиента,

1 - номер сессии,

2 - номер устройства, куда

подключен клиент */

ASIF12RLU user2 2 3

ASIF6RLU END /* END указывает, что описание

закончено */

ASIF8RLT BEGIN /* для команды LT, где

BEGIN указывает на начало описания

процесса */

ASIF14RLT 1 user1 1 2 /* 1 - номер процесса,

user1 - имя владельца процесса,

1 - номер сессии,

2 - номер устройства, на котором

запущен процесс */

ASIF15RLT 2 user2 3 -1 /* -1 - процесс не запущен */

ASIF6RLT END /* END указывает, что описание

процесса закончено */

ФУНКЦИИ БИБЛИОТЕКИ СВЯЗИ

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

Особенности использования функций взаимодействия с сервером приложений в FoxPro 2.6 описаны в приложении 2.

В Delphi и Borland C++ Builder (BCB) создан специальный класс Tconnect, методы (Methods) и свойства (Properties) которого обеспечивают взаимодействие с сервером приложений.

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

Синтаксис:

  CONNECT* ConnectTo (char* hostname, char* service);

 CONNECTTO(hostname, port)

 gl_connectto ();

Вызов функции:

CALL gl_connectto (hostname, hlen, services, slen) RETURNING index)

Входные значения:

hostname - имя компьютера, где расположен сервер приложений;

service - наименование сервиса;

port - номер порта;

hlen - длина переменной 'hostname';

slen - длина переменной 'service'.

Возвращаемые значения:

  Переменная типа CONNECT, в которой хранятся параметры установленного соединения.

  Целое число в интервале 0ё 31, служащее идентификатором установленного соединения.

  Чтобы установить соединение с сервером, необходимо:

      1. свойству HostName присвоить имя компьютера, на котором установлен сервер приложений;
      2. свойству PortName присвоить номер порта;
      3. свойству Connected присвоить значение TRUE.

 Переменная index, содержащая идентификатор соединения, или -1 в случае ошибки.

 

  • Функция: CloseConnect

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

Синтаксис:

  int CloseConnect (CONNECT* con);

 CLOSECONN(conid)

 int gl_closeconnect ();

Вызов функции:

CALL gl_closeconnect (index) RETURNING sc

 

Входные значения:

con - переменная типа CONNECT с параметрами соединения;

conid - идентификатор соединения;

index - идентификатор соединения.

 

Возвращаемые значения:

0 в случае успешного завершения, -1 в случае ошибки.

  Чтобы закрыть соединение с сервером приложений необходимо свойству Connected присвоить значение FALSE.

 Переменная sc, содержащая 0 в случае успешного завершения, -1 в случае ошибки.

 

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

Синтаксис:

  int SendCmd (CONNECT* con, char* command);

 SENDCMD(conid, command)

 Function SendCmd(Cmd: String):Boolean;

 bool __fastcall SendCmd(System::AnsiString Cmd1);

 int gl_sendcmd ();

Вызов функции:

CALL gl_sendcmd (index, cmd, len) RETURNING sc

Входные значения:

con - переменная соединения типа CONNECT;

conid - идентификатор соединения;

command - посылаемая команда;

index - идентификатор соединения;

cmd - строка, содержащая посылаемую команду;

len - длина посылаемой команды.

  Cmd - посылаемая команда;

  Cmd1 - посылаемая команда.

Возвращаемые значения:

  TRUE, если функция выполнена успешно, FALSE - в случае ошибки.

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

 

  • Функция: SendData

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

Синтаксис:

  int SendData (CONNECT* con, char* data);

  SENDDATA(conid, data)

  Function SendData(Data: String):Boolean;

  bool __fastcall SendData(System::AnsiString Data1);

 int gl_senddata ();

Вызов функции:

CALL gl_senddata (index, data, len) RETURNING sc

Входные значения:

con - переменная соединения типа CONNECT;

conid - идентификатор соединения;

data - посылаемые данные;

index - идентификатор соединения;

len - длина посылаемых данных.

 

  Data - посылаемые данные;

  Data1 - посылаемые данные.

Возвращаемые значения:

Количество переданных байт в случае успешного завершения, -1 в случае ошибки.

  TRUE, если функция выполнена успешно, FALSE - в случае ошибки.

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

 

  • Функция: WaitData

Назначение: ждет ответные пакеты от сервера приложений и принимает их в общем виде вне зависимости от типа пакета.

Синтаксис:

  char WaitData (CONNECT* con, int TimeOut, char** buf);

 WAITDATA(conid, TimeOut, buf)

 int gl_waitdata ();

Вызов функции:

CALL gl_waitdata (index, TimeOut) RETURNING sc, buf

Входные значения:

con - переменная соединения типа CONNECT;

conid - идентификатор соединения;

TimeOut - время ожидания пакетов;

buf - буфер для присылаемых данных в теле пакета.

index - идентификатор соединения;

 Параметр buf необходимо передавать по ссылке, т.е. с использованием префикса @.

Возвращаемые значения:

Тип ответного пакета или '\0' в случае отсутствия пакетов.

  В момент прихода ответного пакета от сервера приложений, в зависимости от его вида, происходит одно из следующих событий (Event):

OnError, если получено сообщение об ошибке;

OnReply, если получен ответ на команду;

OnData, если получены данные.

  Переменная sc, содержащая тип ответного пакета или '0' в случае отсутствия пакетов. Буфер buf, содержащий присылаемые с сервера приложений данные или NULL в случае отсутствия данных.

 

  • Функция: RetrieveData

Назначение: разбирает пакеты с данными.

Синтаксис:

  int RetrieveData (char* buf, VALUE* val);

 RETRDATA(buf, val)

Входные значения:

buf - буфер с общими данными;

  val - переменная типа VALUE, хранящая значения переменных и констант из ответного пакета типа 'D';

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

Возвращаемые значения:

0 в случае успешного завершения, -1 в случае ошибки.

  -1 в случае ошибки, 1 - значение пустое, 0 - нет.

Тип VALUE находится в заголовочном файле sdk.h и описан следующим образом:

struct {

int type; // тип значения, хранящегося в структуре

union {

char c; // символ CHAR(1)

short int si; // SMALLINT

long int li; // INTEGER, INT

float f; // SMALLFLOAT, REAL

double d; // FLOAT, DOUBLE PRECISION

char* p; // CHAR(N), VARCHAR[(N)], CHARACTER(N)

void* vp; // структура для других типов (не используется)

} val;

int null; // NULL-индикатор (1 - значение пустое, 0 - нет)

} VALUE;

 

  В Delphi тип значения хранится в переменной vtype, так как в Object Pascal нельзя использовать в качестве имени переменной служебное слово type.

 

3. ХРАНИМЫЕ ПРОЦЕДУРЫ СЕРВЕРА ПРИЛОЖЕНИЙ

Хранимые процедуры сервера приложений являются основным инструментом, реализующим логику прикладной системы. В рамках трехзвенной архитектуры "клиент-сервер приложений-сервер" такой подход позволяет максимально облегчить создание клиентских приложений и добиться создания так называемого "тонкого клиента". С другой стороны, достигается определенная гибкость в реализации правил обработки данных и их распределении между СУБД, которая может их поддержкой не обладать, и самими хранимыми процедурами сервера приложений. В идеале бизнес-логика приложения реализуется частично на СУБД и частично в процедурах сервера приложений. СУБД следует сделать "ответственной" за целостность данных, максимально используя средства, предоставляемые конкретной реализацией SQL для этой СУБД, а на долю хранимых процедур сервера приложений останутся те функции, которые выходят за пределы возможностей самой СУБД и ее механизмов (триггеров, хранимых процедур БД и т.д.).

Следует отличать хранимые процедуры баз данных и хранимые процедуры сервера приложений. Хранимые процедуры баз данных создаются SQL-оператором CREATE PROCEDURE на языке SPL. Они являются неотъемлемой частью базы данных и хранятся непосредственно в рамках СУБД в пространстве данных конкретной базы данных.

Хранимые процедуры сервера приложений создаются в рамках сервера приложений и делятся на два класса:

  • внутренние хранимые процедуры (текстовые, скриптовые);
  • внешние хранимые процедуры (двоичные, библиотечные).

Далее, если это специально не оговорено, под термином хранимая процедура понимается внутренняя хранимая процедура. Термин внешняя хранимая процедура всегда записывается полностью.

3.1 ВНУТРЕННИЕ ХРАНИМЫЕ ПРОЦЕДУРЫ

Внутренние хранимые процедуры создаются на языке хранимых процедур сервера приложений ASPL (рассмотрено ниже).

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

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

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

Подробнее язык хранимых процедур описан в разд. "Язык хранимых процедур сервера приложений".

3.2 ВНЕШНИЕ ХРАНИМЫЕ ПРОЦЕДУРЫ

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

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

Внешняя хранимая процедура сервера приложений может быть создана с использованием любого языка программирования, компилятор которого позволяет создавать разделяемые библиотеки операционной системы. Для UNIX такие библиотеки, как правило, имеют расширение .so, для Windows .dll.

Инсталляция и администрирование пользовательских библиотек

Процесс инсталляции заключается в размещении пользовательских библиотек в соответствующие каталоги. Возможны два вида пользовательских библиотек: общие и частные. Общие библиотеки пользователей должны находиться в каталоге %AS_DIR%\lib. Там находится библиотека стандартных функций lib.dll. Свои частные библиотеки каждый пользователь должен помещать в специально отведённый для них каталог %AS_DIR%\lib\<имя_пользователя>.

Каждый пользователь имеет доступ только к своим частным библиотекам. Общие библиотеки доступны всем пользователям.

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

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

Замечание. Одновременно можно подключить только одну библиотеку (не считая библиотеки стандартных функций).

Обмен данными между интерпретатором ASPL и внешними хранимыми процедурами

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

Создание пользовательских библиотек

Пользовательские библиотеки создаются по общим правилам построения динамически подключаемых библиотек (см. приложение). Наряду с этим есть некоторые особенности, относящиеся к передаче входных и выходных значений библиотечных функций. Любая библиотечная функция должна в явном виде извлекать входные значения из стека и помещать в стек выходные значения. Это объясняется тем, что интерпретатор заранее не может знать о количестве передаваемых параметров. Поэтому перед передачей управления функции интерпретатор помещает в стек входные значения, не проверяя их количество, а функция, получив управление, извлекает входные значения из стека и проверяет их число. После вычисления выходных значений функция должна поместить их в стек. Для того чтобы отличать библиотечные функции от стандартных функций языка C, все функции, входящие в пользовательские библиотеки, должны иметь префикс as_ и быть объявлены следующим образом:

void as_<имя_функции>(void)

Для извлечения входных значений из стека служит функция Pop(). Она возвращает значение типа VALUE, так как в стеке содержатся значения только этого типа. Для того чтобы привести переменную типа VALUE к требуемому типу, нужно воспользоваться одной из функций вида GetAs<Type>(VALUE *), где Type - требуемый тип. Проверка стека на наличие в нём значений осуществляется с помощью функции IsStackEmpty().

После вычисления выходных значений, они должны быть приведены к типу VALUE с помощью одной из функций вида Make<Type>(VALUE*, <Type>). Затем выходные значения заносятся в стек с помощью функции Push(VALUE*) или Return (VALUE*). Эти функции идентичны.

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

Объявление типа VALUE и прототипы всех функций обработки параметров находятся в файле assdk.h. Приведем их список:

Назначение: проверяет стек на наличие в нём элементов.

Синтаксис:

int IsStackEmpty( void );

Возвращаемые значения: 1, если стек пуст, 0 - в противном случае.

  • Функция: Pop

Назначение: извлекает элемент из стека.

Синтаксис:

VALUE Pop( void );

Возвращаемые значения: переменная типа VALUE.

  • Функции: Push и Return

Назначение: помещают элемент в стек.

Синтаксис:

void Push( VALUE* v );

void Return( VALUE* v );

Входные значения:

v - переменная типа VALUE со значением элемента.

  • Функции: GetAs<Type>

Назначение: приводят переменную типа VALUE к типу <Type>.

Синтаксис:

<CType> GetAs<Type>(VALUE* v);

Здесь CType и Type - одни из следующих типов:

CType

Type

char

Char

short int

Short

long int

Long

float

Float

double

Double

char *

String

Замечание. Типы Short, Float не поддерживаются интерпретатором ASPL. Тип Long соответствует типу Integer языка ASPL.

Входные значения:

v - переменная типа VALUE.

Возвращаемые значения: переменная типа <Type>.

  • Функции: Make<Type>

Назначение: приводят переменную типа <Type> к типу VALUE.

Синтаксис:

void Make<Type>( VALUE* v, <CType> c );

Здесь значения CType и Type совпадают с рассмотренными выше.

Входные значения:

v - переменная типа VALUE;

c - переменная типа <Type>.

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

void as_sin( void ) /*Объявление функции*/

{

VALUE v;

double f;

if ( IsStackEmpty() ) /*Проверка числа параметров*/

AS_ERROR("Illegal use of sin function.");

v = Pop(); /*Извлечение входного значения из стека*/

f = GetAsDouble( &v ); /*Приведение входного значения к типу Double*/

errno = 0;

f = sin(f);

if ( errno != 0 )

AS_ERROR("SIN error."); /*Сообщение об ошибке и выход из функции*/

Drop(&v);

MakeDouble( &v, f ); /*Приведение выходного значения к типу VALUE*/

Return( &v ); /*Занесение выходного значения в стек*/

}

Использование пользовательских библиотек

Для использования функций из пользовательской библиотеки соответствующая библиотека должна быть подключена с помощью оператора OPEN LIBRARY <имя_библиотеки>. После этого функции, входящие в библиотеку, могут быть вызваны оператором CALL:

CALL <имя_функции>(<список_входных_значений>) RETURNING <список_возвращаемых_значений>.

Если функция возвращает только одно значение, то её можно использовать в выражениях, например, f = sin(x).

Замечание. При вызове функций, входящих в пользовательские библиотеки, к имени функции не нужно добавлять префикс as_, так как интерпретатор делает это автоматически. Например, рассмотренную выше функцию as_sin следует вызывать CALL sin(x) RETURNING x. Подробнее о функциях стандартной библиотеки см. приложение 3.

4. ЯЗЫК ХРАНИМЫХ ПРОЦЕДУР СЕРВЕРА ПРИЛОЖЕНИЙ

Внутренние хранимые процедуры создаются на языке хранимых процедур сервера приложений (ASPL - Application Server Program Language), который описан ниже. В текущей версии ASPL является интерпретируемым.

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

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

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

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

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

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

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

4.1 НАИБОЛЕЕ ВАЖНЫЕ ОТЛИЧИЯ ОТ SPL

Язык содержит следующие важнейшие отличия от SPL:

4.2 ЭЛЕМЕНТЫ ЯЗЫКА

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

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

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

Список элементов языка:

АРГУМЕНТ

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

СИНТАКСИС

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

CALL add_col ('customer','newint','integer') ;

ВЫРАЖЕНИЕ

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

СИНТАКИС

 

ИСПОЛЬЗОВАНИЕ

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

АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

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

CHAR

INTEGER

FLOAT

STRING

CHAR

CHAR

INTEGER

FLOAT

STRING

INTEGER

INTEGER

INTEGER

FLOAT

INTEGER

FLOAT

FLOAT

FLOAT

FLOAT

FLOAT

STRING

STR

INTEGER

FLOAT

STRING

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

ОПЕРАТОР УСЕЧЕНИЯ

Оператор CLIPPED отбрасывает все конечные символы, не являющиеся алфавитно-цифровыми (пробелы, табуляции, символы перевода строк и возврата каретки). Если тип выражения не строковый, то операция над ним не выполняется. Иначе результатом будет являться строковое значение. Если операнд имеет значение NULL, результатом будет являться NULL. Пример:

lname CLIPPED

ОПЕРАТОР КОНКАТЕНАЦИИ

С помощью оператора (||) можно связать два произвольных выражения. Результатом будет являться строковое значение. Если один из операндов имеет значение NULL, результатом будет являться NULL. Примеры:

lname || zipcode

file_variable || '.dbg'

'Date:' || TODAY

ВЫРАЖЕНИЕ-КОНСТАНТА

На следующей диаграмме показаны возможные определения констант.

USER определяет имя пользователя, под которым зарегистрировался клиент.

TODAY определяет текущую дату в строковом формате.

CURRENT определяет текущее время в строковом формате.

Примеры выражений-констант:

TODAY

'His first name is'

CURRENT

ВЫЗОВ ФУНКЦИИ В ВЫРАЖЕНИИ

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

Примеры:

LENGTH ('abc') + LENGTH (pvar)

HEX (customer_num)

HEX (LENGTH(123))

TAN (radians)

ABS (-32)

EXP (4,3)

MOD (10,3)

АЛГЕБРАИЧЕСКИЕ ФУНКЦИИ

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

Функция ABS

Возвращает абсолютное значение выражения.

Функция MOD

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

Функция POW

Возвращает значение базы в степени экспоненты.

Функция ROOT

Возвращает квадратный корень аргумента.

Функция ROUND

Возвращает округленное значение аргумента.

Функция SQRT

Возвращает квадратный корень аргумента.

Функция TRUNC

Возвращает усеченное значение аргумента.

ЭКСПОНЕНЦИАЛЬНЫЕ И ЛОГАРИФМИЧЕСКИЕ ФУНКЦИИ

Функции используют один или более аргументов.

Функция EXP

Возвращает экспоненциальное значение аргумента.

Функция LOGN

Возвращает натуральный логарифм аргумента.

Функция LOG10

Возвращает десятичный логарифм аргумента.

ФУНКЦИЯ HEX

Возвращает строковое значение целого аргумента в шестнадцатеричной нотации.

ТРИГОНОМЕТРИЧЕСКИЕ ФУНКЦИИ

Функции используют один или более аргументов.

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

радианы = градусы * Пи / 180; градусы = радианы * 180 / Пи

Функция COS

Возвращает значение косинуса для аргумента в радианах.

Функция SIN

Возвращает значение синуса для аргумента в радианах.

Функция TAN

Возвращает значение тангенса для аргумента в радианах.

Функция ACOS

Возвращает значение арккосинуса для аргумента в радианах.

Функция ASIN

Возвращает значение арксинуса для аргумента в радианах.

Функция ATAN

Возвращает значение арктангенса для аргумента в радианах.

ИДЕНТИФИКАТОР

Используются для поименования переменных, курсоров, процедур и т.д.

ИСПОЛЬЗОВАНИЕ

В отличие от СУБД, игнорирующих регистр, в котором написан оператор SQL, СП чувствителен к регистру указанных идентификаторов.

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

ADA

all

and

any

as

asc

authorization

avg

begin

betwe/en

by

char

character

check

close

cobol

commit

continue

count

create

current

cursor

dec

decimal

declare

delete

desc

distinct

double

end

escape

exec

execute

exists

fetch

float

for

fortran

found

from

go

goto

group

having

in

indicator

insert

int

into

integer

is

language

like

max

min

module

not

null

numeric

of

on

open

option

or

order

pascal

pli

precision

primary

procedure

privileges

public

real

rollback

schema

section

select

set

smallint

some

sql

sqlcode

sqlerror

sum

table

to

union

unique

update

user

values

view

whenever

where

with

work

Также запрещено использование в качестве идентификаторов ключевых слов языка хранимых процедур СП:

ALL

AND

AS

BEGIN

CALL

CHAR

CLIPPED

CLOSE

COMMIT

CONNECT

CONTINUE

CURRENT

CURSOR

DECLARE

DEFINE

DELETE

DIGIT

DISCONNECT

ELSE

END

EXECUTE

EXIT

FETCH

FLOAT

FOR

FOREACH

FREE

FROM

FUNCTION

IF

IMMEDIATE

INSERT

INTEGER

INTO

IS

LET

LETTER

LIBRARY

NOT

NULL

OPEN

OR

PREPARE

PRINT

PRINTS

PROCEDURE

RETURN

RETURNING

ROLLBACK

SELECT

SEND

SQLCODE

SQLSTATE

STEP

STRING

SYSTEM

THEN

TO

UPDATE

USER

USING

WHILE

WORK

ОПЕРАТОР ОТНОШЕНИЯ

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

СИНТАКСИС

< означает меньше;

<= означает меньше либо равно;

> означает больше;

>= означает больше либо равно;

= означает равно;

<> означает не равно;

!= означает не равно.

ИСПОЛЬЗОВАНИЕ

Для типов данных CHAR и STRING сравнение производится на основании таблицы символов и использовании кодировки, в рамках которой указана таблица предшествования.

СТРОКА

Строки являются частью выражений.

СИНТАКСИС

ИСПОЛЬЗОВАНИЕ

Строка не может содержать символов новой строки.

ТИП ДАННЫХ

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

СИНТАКСИС

ИСПОЛЬЗОВАНИЕ

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

INTEGER

Переменная типа INTEGER может хранить произвольное целое число в диапазоне, определенном для переменной типа long int в языке С. Арифметические операции с целыми числами приводят к целому результату.

FLOAT

Переменная типа CHAR может хранить любое вещественное значение в формате с плавающей запятой. Количество значащих цифр совпадает с количеством значащих цифр типа double в языке C. Правила записи числа в формате FLOAT приведены далее в разделе "Число". Арифметические операции с вещественными числами приводят к вещественному результату.

CHAR

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

DEFINE var CHAR;

и

DEFINE var CHAR(n);

В первом случае описана переменная, содержащая только один символ, во втором произвольное их количество. В текущей реализации значение n не является предельным значением для длины сохраняемого значения. При присваивании переменной строкового значения длины m>n происходит неявное переопределение типа данных для переменной:

DEFINE var CHAR(m);

В текущей реализации СП не устанавливается различий между типом данных CHAR(n) и STRING.

STRING

В текущей реализации полностью идентичен типу CHAR(n).

СТРУКТУРИРОВАННЫЙ ТИП ДАННЫХ ARRAY

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

Синтаксис

Использование

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

DEFINE A ARRAY[5][5] OF FLOAT;

A[i][j] означает j-й элемент в i-й строке двумерного массива.

Переменные i и j могут меняться только в пределах тех значений, которые указаны при описании массива.

УСЛОВИЯ

Условия используются для проверки данных на соответствие ими определенным условиям.

СИНТАКСИС

ИСПОЛЬЗОВАНИЕ

Условие - это набор из одного или многих условий, произвольно соединенных операторами AND и OR, NOT.

ОПЕРАТОР NOT

Оператор NOT инвертирует значение, вычисляемое условным оператором. Правила вычисления с оператором NOT:

Символ (?) соответствует неопределенному значению, T - истине, F - ложь.

ОПЕРАТОРЫ AND И OR

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

Символ (?) соответствует неопределенному значению, T - истине, F - ложь.

УСЛОВИЯ СРАВНЕНИЯ

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

Такие типы операторов называют булевыми выражениями. Содержательно, булево выражение представляет собой целое число. Нолю соответствует ложь, все остальные значения рассматриваются как истина. Оператор IS NULL возвращает истину, если значение выражения является NULL.

Примеры:

city = 'San'

city IS NULL 'San'

quantity <= 3

customer_num <> 105

customer_num != 105

ЧИСЛО

Число может представлять собой целое или вещественное значение.

СИНТАКСИС

ИСПОЛЬЗОВАНИЕ

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

4.3 СОЗДАНИЕ ХРАНИМЫХ ПРОЦЕДУР

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

СИНТАКИС

Пример описания хранимой процедуры.

FUNCTION not_much();

DEFINE i, j, k INTEGER;

CALL yes_args(0);

CALL yes_args(1) RETURNING i;

CALL yes_args(3) RETURNING i, j, k;

END FUNCTION;

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

4.4 ОПЕРАТОРЫ

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

  • декларативные операторы;

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

Процедурные операторы выполняют определенные действиями по отношению к переменным или порядку выполнения процедуры.

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

4.5 ПРЕДОПРЕДЕЛЕННЫЕ ПСЕВДОПЕРЕМЕННЫЕ

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

SQLCODE содержит код завершения последнего выполненного SQL-оператора.

SQLSTATE содержит код завершения последнего выполненного SQL-оператора по спецификации X/Open.

USER определяет имя пользователя, под которым зарегистрировался клиент.

TODAY определяет текущую дату в строковом формате.

CURRENT определяет текущее время в строковом формате.

Приложение 1.

Особенности взаимодействия КЛИЕНТОВ с сервером приложений в FoxPro 2.6.

Для осуществления взаимодействия с сервером приложений в FoxPro 2.6 необходимо наличие двух файлов:

  1. Foxconn.exe - программа запуска среды FoxPro.
  2. Foxgen.plb - библиотека, содержащая функции для взаимодействия с сервером приложений.

Запуск среды FoxPro осуществляется с помощью программы foxconn.exe, в качестве параметра которой необходимо передать полный путь к исполняемому файлу fox.exe. Пример:

foxconn.exe c:\fpd26\fox.exe

После запуска среды FoxPro необходимо подключить библиотеку функций взаимодействия с сервером приложений. Для этого нужно выполнить команду SET LIBRARY TO foxgen. Файл foxgen.plb должен находиться в текущем каталоге. После подключения библиотеки, можно использовать содержащиеся в ней функции.

ПРИЛОЖЕНИЕ 2.

ФУНКЦИИ СТАНДАРТНОЙ БИБЛИОТЕКИ

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

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

Вызов функции:

CALL cos(FLOAT) RETURNING FLOAT;

  • Функция: sin

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

Вызов функции:

CALL sin(FLOAT) RETURNING FLOAT;

  • Функция: tan

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

Вызов функции:

CALL tan(FLOAT) RETURNING FLOAT;

  • Функция: acos

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

Вызов функции:

CALL acos(FLOAT) RETURNING FLOAT;

  • Функция: asin

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

Вызов функции:

CALL asin(FLOAT) RETURNING FLOAT;

  • Функция: atan

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

Вызов функции:

CALL atan(FLOAT) RETURNING FLOAT;

  • Функция: exp

Назначение: вычисляет экспоненциальное значение аргумента.

Вызов функции:

CALL exp(FLOAT) RETURNING FLOAT;

  • Функция: log

Назначение: вычисляет натуральный логарифм аргумента.

Вызов функции:

CALL log(FLOAT) RETURNING FLOAT;

  • Функция: log10

Назначение: вычисляет десятичный логарифм аргумента.

Вызов функции:

CALL log10(FLOAT) RETURNING FLOAT;

  • Функция: sqrt

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

Вызов функции:

CALL sqrt(FLOAT) RETURNING FLOAT;

  • Функция: open

Назначение: открывает файл в заданном режиме:

"R" - режим чтения;

"W" - режим записи;

"RW", "WR" - режим чтения и записи.

Вызов функции:

DEFINE handle INTEGER; /* дескриптор файла */

DEFINE fname STRING; /* имя файла */

DEFINE mode STRING; /* режим открытия */

:

CALL open(fname, mode) RETURNING handle;

или

CALL open(fname) RETURNING handle;

  • Функция: pipe

Назначение: открывает программный канал.

Вызов функции:

DEFINE hIn, hOut INTEGER; /* входной и выходной дескрипторы

канала */

DEFINE PipeName STRING; /* имя канала */

:

CALL pipe() RETURNING hIn, hOut;

или

CALL pipe(PipeName) RETURNING hIn, hOut;

  • Функция: pcon

Назначение: для совместимости с Windows. Выполняет соединение с каналом.

Вызов функции:

DEFINE pipedes INTEGER; /* дескриптор канала */

:

CALL pcon(pipedes);

  • Функция: pdcon

Назначение: для совместимости с Windows. Выполняет отсоединение от канала.

Вызов функции:

DEFINE pipedes INTEGER; /* дескриптор канала */

:

CALL pdcon(pipedes);

  • Функция: read

Назначение: читает данные из программного канала.

Вызов функции:

DEFINE hOut INTEGER; /* дескриптор канала */

DEFINE size INTEGER; /* требуемое количество байт */

DEFINE s STRING; /* строка с прочитанными данными */

:

CALL read(hOut, size) RETURNING s;

или

CALL read(hOut) RETURNING s;

  • Функция: write

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

Вызов функции:

DEFINE hOut INTEGER; /* дескриптор канала */

DEFINE size INTEGER; /* требуемое количество байт */

DEFINE s STRING; /* строка с записываемыми данными */

:

CALL write(hOut, s, size) RETURNING size;

или

CALL write(hOut, s) RETURNING size;

  • Функция: close

Назначение: закрывает файл или программный канал.

Вызов функции:

DEFINE handle INTEGER; /* дескриптор файла или канала */

:

CALL close(handle);

  • Функция: semget

Назначение: получает дескриптор семафора.

Вызов функции:

DEFINE semname STRING; /* имя семафора */

DEFINE semcount INTEGER; /* количество состояний семафора */

DEFINE semhandle INTEGER; /* дескриптор семафора */

:

CALL semget(semname) RETURNING semhandle;

или

CALL semget(semname, semcount) RETURNING semhandle;

  • Функция: semlock

Назначение: блокирует семафор.

Вызов функции:

DEFINE semhandle INTEGER; /* дескриптор семафора */

DEFINE timeout INTEGER; /* задержка в миллисекундах */

DEFINE lock_result INTEGER; /* 1 - семафор заблокирован,

0 - семафор не заблокирован*/

:

CALL semlock(semhandle) RETURNING lock_result;

или

CALL semlock(semhandle, timeout) RETURNING lock_result;

  • Функция: semtry

Назначение: определяет, можно ли заблокировать семафор.

Вызов функции:

DEFINE semhandle INTEGER; /* дескриптор семафора */

DEFINE i INTEGER; /* 1 - семафор можно заблокировать,

0 - семафор нельзя заблокировать*/

:

LET i = semtry(semhandle);

  • Функция: semunlock

Назначение: освобождает семафор.

Вызов функции:

DEFINE semhandle INTEGER; /* дескриптор семафора */

DEFINE unlock_result INTEGER; /* 1 - семафор освобожден,

0 - семафор не освобожден */

:

CALL semunlock(semhandle) RETURNING unlock_result;

  • Функция: semclose

Назначение: закрывает семафор.

Вызов функции:

DEFINE semhandle INTEGER; /* дескриптор семафора */

:

CALL semclose(semhandle);

  • Функция: stdin

Назначение: возвращает дескриптор стандартного входа.

Вызов функции:

DEFINE stdin_handle INTEGER; /* дескриптор стандартного

входа */

:

CALL stdin() RETURNING stdin_handle;

  • Функция: select

Назначение: ждет изменений состояния дескриптора стандартного входа.

Вызов функции:

DEFINE h ARRAY [*] OF INTEGER; /* массив дескрипторов */

DEFINE timeout INTEGER; /* задержка */

DEFINE ActiveHandleNum INTEGER; /* индекс дескриптора с

изменившимся состоянием */

:

CALL select(h) RETURNING ActiveHandleNum;

или

CALL select(h, timeout) RETURNING ActiveHandleNum;

  • Функция: getpid

Назначение: возвращает идентификатор процесса.

Вызов функции:

DEFINE mypid INTEGER; /* идентификатор процесса */

:

LET mypid = getpid();

  • Функция: sleep

Назначение: останавливает работу процесса на определенное время.

Вызов функции:

DEFINE timeout INTEGER; /* задержка в миллисекундах */

:

CALL sleep(timeout);

  • Функция: kbhit

Назначение: проверяет, есть ли символ на стандартном входе.

Вызов функции:

DEFINE i INTEGER; /* 1 - символ есть,

0 - символа нет */

:

LET i = kbhit();

  • Функция: database

Назначение: для совместимости с INFORMIX версии меньшей 6. Открывает базу данных.

Вызов функции:

DEFINE dbname STRING; /* имя базы данных */

:

CALL database(dbname);

  • Функция: strtok

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

Вызов функции:

DEFINE token STRING; /* лексема */

DEFINE str STRING; /* строка */

DEFINE delimiters STRING; /* ограничители */

:

LET token = strtok(str, delimiters);

или

LET token = strtok(delimiters);

  • Функция: strlen

Назначение: определяет длину строки.

Вызов функции:

DEFINE len INTEGER; /* длина строки */

DEFINE str STRING; /* строка */

:

LET len = strlen(str);

или

CALL strlen(str) RETURNING len;

 

ПРИЛОЖЕНИЕ 3.

Синтаксис операторов языка ASPL

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

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

CALL

Оператор используется для выполнения хранимой процедуры СП или внешней хранимой процедуры СП.

СИНТАКСИС

 

 

 

 

Элемент

Назначение

Ограничения

Синтаксис

Имя переменной

Переменная цикла

Должна быть определена

Идентификатор

Имя процедуры

Уникально идентифицирует вызываемую хранимую процедуру

Должна быть создана хранимая процедура (внешняя хранимая процедура) с таким именем

Идентификатор

ИСПОЛЬЗОВАНИЕ

Оператор используется для вызова хранимой процедуры СП (внешней хранимой процедуры СП).

АРГУМЕНТЫ

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

ПОЛУЧЕНИЕ ЗНАЧЕНИЙ

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

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

В следующем примере показано три вызова хранимой процедуры yes_args с получением от нее разного количество возвращаемых значений.

FUNCTION not_much();

DEFINE i, j, k INTEGER;

CALL yes_args(0);

CALL yes_args(1) RETURNING i;

CALL yes_args(3) RETURNING i, j, k;

END FUNCTION;

CLOSE

Оператор используется для закрытия ставшего ненужным курсора с SELECT-выражением или курсора для вставки.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя курсора

Указывает иденти-фикатор курсора

Должен существовать указанный идентификатор

Идентифи-катор

Переменная курсора

Значение содержит идентификатор курсора

Должен существовать указанный идентификатор

Идентифи-катор

ИСПОЛЬЗОВАНИЕ

Закрытие курсора делает невозможным более доступ к нему любыми операторами, кроме OPEN и FREE, и освобождает ресурсы СП, выделенные для его обработки. Курсор может быть закрыт, если он не был открыт, а также закрыт повторно.

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

ОБРАБОТКА ОШИБОК

После выполнения оператора, код его завершения доступен через переменные SQLCODE и SQLSTATE.

ССЫЛКИ

Смотри также разделы с описанием операторов DECLARE, FETCH, FREE, OPEN.

CLOSE LIBRARY

Оператор используется для открытия библиотеки внешних хранимых процедур Синтаксис

 

 

 

 

Элемент

Назначение

Ограничения

Синтаксис

Имя библиотеки

Указывает имя библиотеки внешних процедур

Выражение, результат которого - строковая константа

Идентификатор

Переменная библиотеки

Значение содержит имя библиотеки внешних процедур

Должен существовать указанный идентификатор

Идентификатор

ИСПОЛЬЗОВАНИЕ

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

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

CONTINUE

Оператор используется для перехода к следующей итерации циклов FOR, WHILE или FOREACH.

СИНТАКСИС

ИСПОЛЬЗОВАНИЕ

После выполнения оператора управление передается на начало тела самого вложенного цикла указанного типа. Все операторы после CONTINUE в рамках этого цикла пропускаются.

В следующем примере оператор INSERT не выполнится при i равном 11.

FUNCTION loop_skip();

DEFINE i INTEGER;

.

.

FOR i = 1 TO 15 STEP 2

IF i == 11 THEN

CONTINUE FOR;

END IF;

INSERT INTO testtable VALUES( i, null );

END FOR;

RETURN i;

END FUNCTION;

Оператор CONTINUE сгенерирует ошибку при использовании его за пределами цикла.

DECLARE

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

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя курсора

Идентифицирует курсор

Уникально в переделах процедуры

Идентификатор

Переменная курсора

Значение содержит идентификатор курсора

Должен существовать указанный идентификатор

Идентификатор

Имя переменной

Содержит SQL-оператор

Должна быть описана

Идентификатор

ИСПОЛЬЗОВАНИЕ

Оператор DECLARE ассоциирует курсор с SQL-оператором и назначает курсору уникальный в пределах процедуры идентификатор. До начала работы с курсором он должен быть описан.

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

Иначе за курсором закрепляется указанный идентификатор.

ОБРАБОТКА ОШИБОК

После выполнения оператора, код его завершения доступен через переменные SQLCODE и SQLSTATE.

ССЫЛКИ

Смотри также разделы с описанием операторов CLOSE, FETCH, FREE, OPEN.

DEFINE

Оператор используется для описания переменных и ассоциирования с ними определенных типов данных.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя переменной

Переменная цикла

Должна быть определена

Идентификатор

Тип данных

Указывает тип переменной

Ограничено типами данных, поддерживаемыми СП

Зарезервирован-ное слово

ИСПОЛЬЗОВАНИЕ

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

Переменная с одним и тем же именем может быть в дальнейшем переописана с помощью оператора REDEFINE. После описания переменная получает значение NULL. Пример:

FUNCTION example1();

DEFINE n INTEGER; DEFINE j INTEGER;

.

DEFINE a CHAR;

DEFINE b ARRAY[5][10] OF FLOAT;

. . .

EXIT

Оператор используется для выхода из циклов FOR, WHILE и FOREACH.

СИНТАКСИС

ИСПОЛЬЗОВАНИЕ

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

Оператор EXIT сгенерирует ошибку при использовании его за пределами цикла или при отсутствии объявления цикла соответствующего типа.

В следующем примере цикл FOR прекратит выполнение, когда в цикле WHILE переменная i достигнет значения 5.

FUNCTION ex_cont_ex();

DEFINE i,j,s INTEGER;

FOR j = 1 TO 20

IF j>10 THEN

CONTINUE FOR;

END IF;

LET i = j;

LET s = 0;

WHILE i > 0

LET i = i - 1;

IF i == 5 THEN

EXIT FOR;

END IF;

END WHILE;

END FOR;

END FUNCTION;

FETCH

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

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя курсора

Указывает идентификатор курсора

Должен существовать указанный идентификатор

Идентифи-катор

Переменная курсора

Значение содержит идентификатор курсора

Должен существовать указанный идентификатор

Идентифи-катор

ИСПОЛЬЗОВАНИЕ

Оператор FETCH используется для обработки запросов, возвращающих более одной строки результата. Всего таких операторов четыре: FETCH, DECLARE, OPEN, CLOSE.

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

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

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

ОБРАБОТКА ОШИБОК

После выполнения оператора, код его завершения доступен через переменные SQLCODE и SQLSTATE.

ССЫЛКИ

Смотри также разделы с описанием операторов CLOSE, DECLARE, FREE, OPEN.

FOR

Оператор используется для создания безусловных циклов.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Выражение

Используется для задания начального, конечного значений переменной цикла и шага цикла

Должно быть целочисленным

Выражение

Имя переменной

Переменная цикла

Должна быть определена

Идентификатор

ИСПОЛЬЗОВАНИЕ

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

Пример использования вложенных циклов.

FUNCTION for_ex();

DEFINE i, j INTEGER;

FOR i = 1 TO 10

FOR j = i TO 10

INSERT INTO tab VALUES( i, j );

END FOR;

END FOR;

END FUNCTION;

FOREACH

Оператор используется для получения набора многострочных данных.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя курсора

Идентифицирует курсор

Уникально в переделах процедуры

Идентификатор

Переменная курсора

Значение содержит идентификатор курсора

Должен существовать указанный идентификатор

Идентификатор

ИСПОЛЬЗОВАНИЕ

Использование оператора FOREACH аналогично использованию аппарата курсоров. При выполнении оператора сервер приложений выполняет следующие операции:

Операторы FOREACH могут быть вложенными. Ограничений на уровень вложенности нет.

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

В следующих примерах показано использование оператора FOREACH.

FUNCTION foreach_ex();

DEFINE i, j INTEGER;

FOREACH SELECT c1 INTO i FROM tab ORDER BY 1

INSERT INTO tab2 VALUES ( i );

END FOREACH;

FOREACH cur1 FOR SELECT c2, c3 INTO i, j FROM tab

IF j > 100 THEN

DELETE FROM tab WHERE CURRENT OF cur1;

CONTINUE FOREACH;

END IF;

UPDATE tab SET c2 = c2 + 10 WHERE CURRENT OF cur1;

END FOREACH;

FOREACH EXECUTE PROCEDURE bar(10,20) INTO i

INSERT INTO tab2 VALUES( i );

END FOREACH;

END FUNCTION;

ОБРАБОТКА ОШИБОК

После выполнения оператора, код его завершения доступен через переменные SQLCODE и SQLSTATE.

ССЫЛКИ

Смотри также разделы с описанием операторов CLOSE, DECLARE, FETCH, FREE, OPEN.

FREE

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

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя курсора

Идентифицирует курсор

Уникально в переделах процедуры

Идентифи-катор

Переменная курсора

Значение содержит идентификатор курсора

Должен существовать указанный идентификатор

Идентифи-катор

ИСПОЛЬЗОВАНИЕ

Оператор освобождает ресурсы, занятые сервером приложений и СУБД. Рекомендуется закрывать курсор перед использованием оператора FREE. После использования оператора FREE курсор не может быть открыт оператором OPEN снова. Для этого необходимо снова использовать оператор DECLARE.

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

ОБРАБОТКА ОШИБОК

После выполнения оператора, код его завершения доступен через переменные SQLCODE и SQLSTATE.

ССЫЛКИ

Смотри также разделы с описанием операторов CLOSE, DECLARE, FETCH, OPEN.

IF

Оператор используется для создания альтернативных ветвей выполнения программы.

СИНТАКСИС

ИСПОЛЬЗОВАНИЕ

В зависимости от значения выражения выполняется ветка THEN или ELSE. Значение выражения приводится к целому типу. Если результат приведения равен 0, выполняется ветка ELSE. Значение NULL приводится к нулю.

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

FUNCTION str_compare( str1, str2 );

DEFINE result INTEGER;

IF str1 > str2 THEN

LET result = 1;

ELSE

IF str1 < str2 THEN

LET result = -1;

ELSE

LET result = 0;

END IF;

END IF;

RETURN result;

END FUNCTION ;

LET

Оператор используется для присваивания переменным и элементам массива новых значений. Этот оператор может быть опущен.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя переменной

Переменная цикла

Должна быть определена

Идентификатор

ИСПОЛЬЗОВАНИЕ

Элементом выражения может выступать вызов хранимой процедуры.

Примеры:

a = c + d;

LET name = 'test' || random();

OPEN

Оператор используется для открытия курсора, ассоциированного с оператором SELECT или EXECUTE PROCEDURE.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя курсора

Указывает идентификатор курсора

Должен существовать указанный идентификатор

Идентифи-катор

Переменная курсора

Значение содержит идентификатор курсора

Должен существовать указанный идентификатор

Идентифи-катор

ИСПОЛЬЗОВАНИЕ

Курсор может быть открыт, если он был описан с помощью оператора DECLARE. В момент открытия курсора ассоциированный с ним SQL-запрос передается СУБД.

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

Курсор может быть переоткрыт в произвольный момент.

Примеры:

DECLARE curs CURSOR FOR SELECT * FROM orders;

OPEN curs;

DECLARE curs CURSOR FOR EXECUTE PROCEDURE proc();

OPEN curs;

ОБРАБОТКА ОШИБОК

После выполнения оператора, код его завершения доступен через переменные SQLCODE и SQLSTATE.

ССЫЛКИ

Смотри также разделы с описанием операторов CLOSE, DECLARE, FETCH, FREE.

OPEN LIBRARY

Оператор используется для открытия библиотеки внешних хранимых процедур.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя библиотеки

Указывает имя библиотеки внешних процедур

Выражение, результат которого - строковая константа

Идентифика-тор

Переменная библиотеки

Значение содержит имя библиотеки внешних процедур

Должен существовать указанный идентификатор

Идентифика-тор

ИСПОЛЬЗОВАНИЕ

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

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

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

В следующем примере открывается внешняя библиотека math и вызывается внешняя функция bessel:

.

.

OPEN LIBRARY 'math';

LET a = bessel(0,1,3);

.

.

REDEFINE

Оператор используется для переописания переменных и ассоциирования с ними других типов данных.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя переменной

Переменная цикла

Должна быть определена

Идентификатор

Тип данных

Указывает тип переменной

Ограничено типами данных, поддерживаемыми СП

Зарезервирован-ное слово

ИСПОЛЬЗОВАНИЕ

Оператор REDEFINE не является исполняемым и указывается в любом месте процедуры для переописания переменных, ранее описанных с помощью оператора DEFINE.

Переменная с одним и тем же именем может быть в дальнейшем вновь переописана. После описания переменная получает значение NULL. Пример:

FUNCTION example1();

DEFINE n INTEGER; DEFINE j INTEGER;

. . .

REDEFINE n CHAR;

RETURN

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

СИНТАКСИС

ИСПОЛЬЗОВАНИЕ

Оператор RETURN может возвратить ноль или более значений вызвавшему процессу. Во время выполнения хранимой процедуры может быть выполнено несколько операторов RETURN. Пример:

FUNCTION two( i INTEGER, j INTEGER );

IF i > j THEN

RETURN i, j;

ELSE

RETURN j, i;

END IF;

RETURN i > j;

END FUNCTION;

SEND

Оператор используется для передачи пользователю значений выражений.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Выражение

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

Нет

Выражение

ИСПОЛЬЗОВАНИЕ

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

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

FUNCTION send_i( i INTEGER );

SEND "sqr(i) == " || i*i,

"cube(i) == " || i*i*i;

END FUNCTION;

SET CONNECT TO

Оператор используется для установки активного соединения, идентификатор которого был указан в операторе CONNECT после ключевого слова AS.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Имя соединения

Идентифицирует соединение

Уникально в переделах процедуры

Идентификатор

Переменная соединения

Значение содержит идентификатор соединения

Должен существовать указанный идентификатор

Идентификатор

ИСПОЛЬЗОВАНИЕ

Клиент может установить несколько соединений с разными базами данных. Чтобы переключаться между соединениями, он должен использовать оператор SET CONNECT TO.

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

FUNCTION set_connection();

DEFINE s2 STRING;

s2 = 'c2';

CONNECT TO 'db1' as 'c1';

CONNECT TO 'db2' as s2;

SET CONNECT TO 'c1';

END FUNCTION;

SYSTEM

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

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

Выражение

Определяет командную строку, содержащую команду операционной системы

Нет

Выражение

Имя переменной

В первом случае содержит команду операционной системы,

Во втором после выполнения команды содержит код ее завершения

Нет

Идентифика-тор

ИСПОЛЬЗОВАНИЕ

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

FUNCTION cat();

DEFINE i INTEGER;

SYSTEM 'ls | wc -l' RETURNING i;

RETURN i;

END FUNCTION;

WHILE

Оператор используется для создания условных циклов в программе.

СИНТАКСИС

Элемент

Назначение

Ограничения

Синтаксис

выражение

определяет возможность следующей итерации

Нет

выражение

ИСПОЛЬЗОВАНИЕ

Перед началом каждой итерации значение выражения вычисляется заново. Если значение выражения является истиной, следующая итерация цикла выполняется. Здесь выражение имеет тот же смысл, что и выражение в операторе IF.

Выполнение оператора может быть досрочно завершено оператором EXIT WHILE. Пример использования:

FUNCTION simp_while();

DEFINE i INTEGER;

LET i = 1;

WHILE i < 10

INSERT INTO tab VALUES ( i, i+1 );

LET i = i + 1;

END WHILE;

END FUNCTION;

Приложение 4.

ПРИМЕР ИСПОЛЬЗОВАНИЯ СЕРВЕРА ПРИЛОЖЕНИЙ

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

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

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

Данный пример может служить основой для построения сложных производственных систем с использованием возможностей сервера приложений.

ПОСТАНОВКА ЗАДАЧИ

Целью данной работы является построение приложения, отвечающего следующим требованиям:

  1. Обеспечение многоуровневой, распределенной обработки информации, поступающей от клиентов. В качестве такой информации клиент посылает идентификатор установки для инициализации технологического процесса. Последующая информация представляет собой набор данных, получаемых в результате технологического процесса. Эти данные обрабатываются на сервере приложений.
  2. Получение от сервера информации, определяющей дальнейшие действия технологического процесса в зависимости от результатов обработки данных.
  3. Занесение обработанной информации на сервер баз данных.
  4. Удаленный мониторинг и управление работой клиентов. В этом случае сервер получает информацию, поступающую от клиента, обрабатывает ее и при необходимости посылает команды клиенту.
  5. Получение определенной статистической информации из базы данных для администратора.

ОПИСАНИЕ ВЗАИМОДЕЙСТВИЯ

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

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

ОБЩАЯ СХЕМА ВЗАИМОДЕЙСТВИЯ

Общая схема взаимодействия клиентов с сервером приложений показана на рис. 4.

Рис. 4. Схема взаимодействия клиентов, сервера приложений и СУБД.

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

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

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

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

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

Перед тем как послать данные подсистема ввода/вывода клиентской части передает подсистеме обработки информации администратора соответствующую команду ('D') и идентификатор сессии клиента. В случае положительного ответа от подсистемы обработки информации клиентской части клиент посылает данные.

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

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

  • Команда, определяющая количество работающих устройств

simple

  • Команда, определяющая информацию по отдельному устройству

info <номер_устройства>

  • Команда, определяющая количество устройств, работавших в определенный промежуток времени

interval <начальная_дата>,<конечная_дата>

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

yyyy-mm-dd HH:MM:SS.FRC,

где yyyy - год (4 цифры),

mm - месяц (2 цифры),

dd - день (2 цифры),

HH - часы (2 цифры),

MM - минуты (2 цифры),

SS - секунды (2 цифры),

FRC - доли секунды (3 цифры).

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

Подробнее о типах информации, передаваемой между сторонами распределенной системы, см. на рис. 5.

Рис. 5. Обмен информацией между клиентами, сервером приложений и СУБД.

На рис. 5 использованы следующие условные обозначения:

подсистема1 - подсистема ввода/вывода клиентской части;

подсистема2 - подсистема ввода/вывода администратора;

подсистема3 - подсистема обработки информации клиентской части;

подсистема4 - подсистема обработки информации администратора.

ОПИСАНИЕ ПРОГРАММ. ФУНКЦИИ СЕРВЕРА

ФУНКЦИЯ SERVER()

Осуществляет подготовку каналов к работе и обработку команд клиентов.

{--------------------------------------------------}

  1. function Server()

{--------------------------------------------------}

define ClNum integer;

let ClNum = 20;

global define clnums array[ClNum] of character;

global define inchan array[ClNum+1] of integer;

global define outchan array[ClNum+1] of integer;

global define sems array[ClNum+1] of integer;

global define opened integer;

define qsem integer;

define i, j, cnt integer;

define sellst array[2] of integer;

define cmd character;

define uid integer;

define str string;

define f float;

define Master integer;

define nl char;

nl=10;

  1. let Master = ClNum+1;
  2. if opened IS NULL then let opened=0; end if;

    if not opened then

  3. for i=1 to ClNum
  4. let clnums[i] = 'F';

    call pipe("pipe"||i) returning inchan[i],outchan[i];

    let sems[i] = semget("sem"||i);

    end for;

  5. call pipe("master") returning inchan[Master], outchan[Master];
  6. let sems[Master] = semget("semmaster");

    let qsem = semget("qmaster");

    let j = semlock(sems[Master]);

    let opened = 1;

    end if;

  7. let sellst[1] = sems[Master];
  8. let sellst[2] = stdin();

    while 1

  9. let i = select(sellst);
  10. if i == -1 then

call WinError("select() error:");

return;

end if;

if i == 1 then

call pcon(inchan[Master]);

let cmd = read(inchan[Master],1);

let uid = read(inchan[Master]);

send "Received command '"||cmd||"' from process "||uid,nl;

  1. if cmd == 'R' then
  2. for i=1 to ClNum

    if clnums[i] == 'F' then

    exit for;

    end if;

    end for;

    if i > ClNum then

    let j=write(outchan[Master],"E");

    else

    let j=write(outchan[Master],"R"||i);

    let clnums[i] = 'T';

    end if;

    end if;

  3. if cmd == 'U' then
  4. if clnums[uid] == 'T' then

    LET clnums[uid] = 'F';

    let j=write(outchan[Master], "U");

    else

    let j=write(outchan[Master], "E");

    end if;

    end if;

  5. if cmd == 'D' then
  6. call pcon(inchan[uid]);

    let str = read(inchan[uid]);

    send "Session " || uid || " new data: " || str, nl;

    call pdcon(inchan[uid]);

    call close(inchan[uid]);

    call pipe("pipe"||uid) returning inchan[uid], outchan[uid];

    end if;

  7. call pdcon(inchan[Master]);
  8. call close(inchan[Master]);

    call pipe("master") returning inchan[Master], outchan[Master];

    else

  9. if kbhit(sellst[2]) then

exit while;

end if;

end if;

end while;

end function;

  1. Функция Server() осуществляет подготовку каналов сессий к работе и обработку команд клиентов (подробнее о сессиях см. раздел "Общая схема сервера"). Оператор DEFINE определяет следующие глобальные переменные:

    • clnums - массив флагов каналов;
    • inchan - массив дескрипторов для чтения каналов клиента и основного канала;
    • outchan - массив дескрипторов для записи каналов клиента и основного канала;
    • sems - массив семафоров клиентских каналов и основного канала;
    • ClNum - количество каналов.

  1. Определение номера основного канала, соответствующего основной сессии.
  2. Подготовка к работе каналов и их семафоров.
  3. Открытие основной сессии для первоначального взаимодействия клиента с сервером приложений.
  4. Подготовка массива дескрипторов.
  5. Ожидание запроса со стороны клиента через подсистему ввода/вывода клиентской части.
  6. Обработка запроса:

    • Если запрос не поступил, то сообщение об ошибке.
    • Иначе подсистема обработки информации администратора:
    • устанавливает соединение через канал основной сессии с подсистемой обработки информации клиентской части;
    • получает команду и идентификатор установки;
    • передает сообщение о запросе клиента подсистеме ввода/вывода администратора.

  1. В случае поступления команды о регистрации клиента осуществляется поиск свободного канала. Если он есть, то подсистема обработки информации администратора посылает клиенту идентификатор сессии, открытой на этом канале. Иначе передается сообщение об ошибке.
  2. В случае поступления команды на отключение клиента осуществляется освобождение канала или передается сообщение об ошибке.
  3. В случае поступления команды о передаче данных подсистема обработки информации администратора подключается к сессии клиента, получает данные, передает их подсистеме ввода/вывода администратора и отключается от клиентской сессии.
  4. Отключение клиентской части от основной сессии.
  5. Проверка наличия команд на входе подсистемы обработки информации администратора.

ФУНКЦИЯ SERVCMD()

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

{--------------------------------------------------}

  1. function ServCmd( sid integer, s string )
  2. {--------------------------------------------------}

    define his,hos integer;

    define i, cnt, dn integer;

    define ctype string;

    define dnum, dtbeg, dtend, dt, dt1, sessid string;

    define nl character;

    let nl = 10;

  3. let ctype = strtok(s," ");
  4. if NOT ctype IS NULL then

  5. if ctype == "simple" then
  6. let cnt = 0;

    for i=1 to 20 -- ClNum

    if clnums[i] == 'T' then

    let cnt = cnt+1;

    end if;

    end for;

    send "there are " || cnt || " working devices at this moment."||nl;

    return;

    end if;

  7. if ctype == "info" then
  8. connect to 'astest';

    let dnum = strtok(" ");

    if dnum IS NULL then

    send "Device number is not specified.";

    return;

    end if;

  9. select data.sid, time into i, dtbeg from data
  10. where time =

    ( select max(time) from data

    where devnum == dnum

    and param == -1 );

    if SQLCODE < 0 then

    send "select error #"||SQLCODE||nl;

    disconnect current;

    return;

    end if;

    if SQLCODE == 100 then

    send "No information about device "||dnum||nl;

    disconnect current;

    return;

    end if;

    let dtbeg = dtbeg CLIPPED;

    select time into dtend from data

    where time > dtbeg

    and data.sid == i

    and param == -2;

    if SQLCODE < 0 then

    send "2-nd select error #"||SQLCODE||nl;

    disconnect current;

    return;

    end if;

    if SQLCODE != 100 then

    send "Device "||dnum||" is in offline from "||dtend

    CLIPPED||"."||nl;

    disconnect current;

    return;

    end if;

  11. send "Device "||dnum||" is online from "||dtbeg||

select count(*) into cnt from data

where time > dtbeg

and data.sid == i;

send "Already has been inserted "|| cnt ||" parameters."||nl;

disconnect current;

return;

end if;

  1. if ctype == "interval" then
  2. connect to 'astest';

    let cnt = 0;

    let dnum = 0;

    dtbeg = strtok(",") CLIPPED;

    dtend = strtok(",") CLIPPED;

    send "Devices worked from "||dtbeg||" till "||dtend||":"||nl;

  3. declare cur cursor for
  4. select time, data.sid, devnum from data

    where time <= dtend

    and param == -1;

    foreach cur into dt, i, sessid

    let dt = dt clipped;

    select min(time) into dt1 from data

    where time > dt

    and data.sid == i

    and param in (-1,-2)

    and time > dtbeg;

    if SQLCODE < 0 then

    send "3-rd select error #"||SQLCODE||nl;

    return;

    end if;

    if dt1 IS NULL then

    select min(time) into dt1 from data

    where time > dt

    and data.sid == i

    and param in (-1,-2);

    if dt1 IS NULL then

    send "Device ",sessid CLIPPED," with session id ",i,

    " has begun work at ",dt," and possibly working now",nl;

    let dnum = dnum+1;

    end if;

    else

  5. send "Device ",sessid CLIPPED," with session id ",i,

" has begun work at ",dt," and finished at ",dt1,nl;

let dnum = dnum+1;

end if;

end foreach;

send "There are " || dnum || " devices was working at this

period."||nl;

disconnect current;

return;

end if;

end if;

  1. if clnums[sid] != 'T' then
  2. send "Error: no process with sid "||sid;

    return;

    end if;

  3. let his = inchan[sid];
  4. let hos = outchan[sid];

    let i=semunlock(sems[sid]);

    call pcon(hos);

    let i=write(hos, s);

    call pdcon(his);

    call close(his);

    call pipe("pipe"||sid) returning inchan[sid], outchan[sid];

  5. send "Command "||s||" is sent.";

end function;

  1. Функция ServCmd() осуществляет управление работой клиента с помощью команд. В функцию передаются два параметра:

    • sid - идентификатор клиентской сессии;
    • s - строка с командой для клиента.

  1. Определение типа поступившей команды.
  2. Если поступившая команда - это 'simple', то подсистема обработки информации администратора подсчитывает количество работающих устройств в настоящий момент. Затем посылает администратору сообщение о полученном количестве.
  3. Если поступившая команда - это 'info', то подсистема обработки информации администратора сначала проверяет, указан ли номер устройства. Если нет, то администратору посылается соответствующее сообщение и функция завершает свою работу.
  4. Если устройство указано, то подсистема обработки информации администратора обращается к базе данных с запросами о времени начала работы указанного устройства и о конечном времени работы этого устройства. Если в результате любого из запросов возникает ошибка или не найдена информация по указанному устройству, то функция завершает свою работу.
  5. Администратору посылается информация о начале работы указанного устройства и номер сессии для этого устройства. А также сообщается количество записанной информации в течение указанной сессии.
  6. Если поступившая команда - это 'interval', то подсистема обработки информации администратора сначала определяет начальное и конечное время, указанное в параметрах данной команды.
  7. С помощью курсора определяется количество устройств, начавших или закончивших работу в течение времени, указанного в параметрах команды 'interval'.
  8. Администратору передаются сообщения с информацией об идентификаторе сессии и начальном и конечном времени работы каждого устройства. В конце выполнения команды сообщается общее количество работающих устройств.
  9. Проверка идентификатора клиентской сессии. Если нет сессии с таким идентификатором, то выход.
  10. Если сессия открыта и существует клиентский канал, то подсистема обработки информации администратора устанавливает соединение через этот канал, посылает клиенту команду и отключается от клиентского канала.
  11. Подсистема обработки информации администратора посылает подсистеме ввода/вывода администратора сообщение о том, что клиенту отправлена команда.

ФУНКЦИЯ INIT()

Определяет начальные данные для работы клиента с сервером.

{--------------------------------------------------}

  1. function Init( id integer )
  2. {--------------------------------------------------}

    global define semm, him, hom, qsem integer;

    global define sems integer;

    global define min_bound, max_bound float;

    define his, hos, i integer;

    define answ character;

    define sid, cnt, delay integer;

  3. let semm=semget("semmaster");
  4. let qsem=semget("qmaster");

    call pipe("master") returning him, hom;

    let i=semlock(qsem);

    let i=semunlock(semm);

    let i=write(hom, "R"||getpid());

    let answ=read(him,1);

    if answ == 'R' then

    let sid=read(him);

    else

    send "Bad answer from server.";

    return NULL, NULL, NULL;

    end if;

  5. let sems = semget("sem"||sid);
  6. let i=semunlock(qsem);

  7. connect to "astest";
  8. if SQLCODE != 0 then

    send "Connection error #" || SQLCODE;

    end if;

    select c, d, bmin, bmax into cnt, delay, min_bound, max_bound from schrs

    where unit_id == id;

  9. if SQLCODE != 0 then
  10. send "Can't retrieve proper information, SQLCODE = "||SQLCODE||".";

    return NULL, NULL, NULL;

    end if;

  11. INSERT INTO data VALUES( CURRENT, -1, id, sid );
  12. if SQLCODE != 0 then

    send "Insert error #" || SQLCODE;

    end if;

  13. return sid, cnt, delay;

end function;

  1. Функция Init() определяет начальные данные для работы клиента с сервером. В функцию передается один параметр:

    • id - идентификатор установки.

Оператор DEFINE определяет следующие глобальные переменные:

    • semm, him, hom, qsem - дескрипторы и семафоры основного канала, соответствующего основной сессии;
    • sems - семафор клиентского канала;
    • min_bound, max_bound - границы значений для клиентских данных.

  1. Подсистема обработки информации клиентской части получает идентификатор основной сессии и открывает основной канал. Клиент посылает команду на регистрацию и идентификатор установки, получает ответ:

    • в случае положительного ответа клиент получает идентификатор сессии свободного канала;
    • иначе - сообщение об ошибке.

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

    • CURRENT - время;
    • -1 означает начало работы с клиентом;
    • id - идентификатор установки;
    • sid - идентификатор сессии.

  1. Возвращение требуемых значений.

ФУНКЦИЯ NEWDATA()

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

{--------------------------------------------------}

  1. function NewData( sid integer, data float )
  2. {--------------------------------------------------}

    define i integer;

    define a array[2] of integer;

    define his,hos integer;

    define datatab string;

  3. let i=semlock(qsem);
  4. let i=semunlock( semm );

    call pipe("master") returning him, hom;

    let i=write( hom, "D"||sid);

    call pipe("pipe"||sid) returning his, hos;

    let i=write(hos, data);

    call close(his);

    let i=semunlock(qsem);

  5. if data >= min_bound and data <=max_bound then
  6. send 1;

    let datatab = "data";

    INSERT INTO datatab VALUES( CURRENT, data, 0, sid );

    if SQLCODE != 0 then

    send "Insert error #" || SQLCODE;

    return;

    end if;

    else

    send 0;

    end if;

  7. let a[1] = sems;
  8. let a[2] = stdin();

    if semtry(sems) then let i=semlock(sems); end if;

    while 1

  9. let i=select(a);

if i==-1 then exit while; end if;

if i==1 then

call client(sid);

else

if kbhit(a[2]) then

exit while;

end if;

end if;

end while;

end function;

  1. Функция NewData() осуществляет обработку данных, поступающих от клиента. В функцию передаются два параметра:

    • sid - идентификатор клиентской сессии;
    • data - строка с клиентскими данными.

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

    • CURRENT - время;
    • data - данные;
    • 0 - идентификатор по умолчанию;
    • sid - идентификатор сессии.

В операторе INSERT переменная datatab содержит имя таблицы "data", чтобы избежать конфликта с переменной data, содержащей клиентские данные. Этот конфликт может возникнуть при явном использовании имени таблицы в операторе INSERT.

  1. Подготовка массивов дескрипторов и предварительная блокировка семафора клиентского канала.
  2. Ожидание команд. В зависимости от поступающих команд происходит обработка клиентского запроса или обработка запроса администратора.

ФУНКЦИЯ UNREG()

Предназначена для передачи на сервер команды клиента о закрытии клиентской сессии и освобождении клиентского канала.

{--------------------------------------------------}

  1. function Unreg( sid integer )
  2. {--------------------------------------------------}

    define i integer;

    define s string;

  3. let i=semlock(qsem);
  4. let i=semunlock(semm);

    call pipe("master") returning him, hom;

    let i=write(hom, "U"||sid);

    let s=read(him);

    if s != "U" then

    send "Unregister error: no session "||sid||".";

    else

    send "Unregistration successfull.";

    end if;

  5. call close(him);
  6. let i=semunlock(qsem);

  7. INSERT INTO data VALUES( CURRENT, -2, 0, sid );

end function;

  1. Функция Unreg() осуществляет передачу на сервер команды клиента о закрытии клиентской сессии и освобождении соответствующего канала. В функцию передается один параметр:

    • sid - идентификатор клиентской сессии.

  1. Подсистема обработки информации клиентской части получает доступ к каналу основной сессии, посылает подсистеме обработки информации администратора идентификатор клиентской сессии и команду об отключении клиента, получая ответ:

    • в случае положительного ответа клиент получает сообщение об успешном отключении;
    • иначе - сообщение об ошибке.

  1. Закрывается сессия основного канала и освобождается ее семафор.
  2. Занесение в базу данных информации о завершении соединения:

    • CURRENT - время;
    • -2 - конец работы с клиентом;
    • 0 - идентификатор по умолчанию;
    • sid - идентификатор сессии.

ОПИСАНИЕ ПРОГРАММ. ПРОГРАММЫ КЛИЕНТА

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

ПРОГРАММА КЛИЕНТА НА C

{--------------------------------------------------}

  1. void main(void)
  2. {--------------------------------------------------}

    {

    CONNECT* con;

    int sc, i, j, inf[3];

    char ch;

    char *cmd, *buf;

    VALUE val;

  3. if ((con = ConnectTo("as_server","shc")) == NULL) {
  4. perror("ConnectTo");

    exit(1);

    }

  5. if ((sc = SendCmd (con, "BS")) == -1) {
  6. perror("SendCmd");

    CloseConnect (con);

    exit(1);

    }

    WaitData (con, 1, &buf);

    printf ("%s\n", buf);

    free (buf);

  7. cmd = "function; define s, c, d integer;";
  8. sc = SendData (con, cmd);

    cmd = "call Init(1) returning s, c, d; send s, c, d;";

    sc = SendData (con, cmd);

    for (i=0;;i++) {

    if ((ch = WaitData (con, 3, &buf)) == '\0') break;

    if (ch == 'D') {

    RetrieveData (buf,&val);

    inf[i] = val.val.li;

    }

    else printf ("%s\n", buf);

    free (buf);

    }

  9. cmd = (char*) malloc (50);
  10. sprintf (cmd, "call NewData(%d,%g);", inf[0], rnd());

    for (i=0; i<inf[1]; i++) {

    SendData (con, cmd);

    free (cmd);

    for (j=0;;j++) {

    if ((ch = WaitData (con, 1, &buf)) == '\0') break;

    if (ch == 'D') {

    RetrieveData (buf, &val);

    PrnData (&val); }

    free (buf);

    }

    if (i<inf[1]-1) {

    cmd = (char*) malloc (50);

    sprintf (cmd, "call NewData(%d,%g);", inf[0], rnd());

    sleep(inf[2]/1000);

    }

    }

    if ((i != inf[1]) && (ch == '\0')) printf ("No answer\n");

  11. cmd = (char*) malloc (20);

sprintf (cmd, "call Unreg(%d);", inf[0]);

sc = SendData (con, cmd);

WaitData (con, 1, &buf);

RetrieveData (buf, &val);

PrnData (&val);

free (buf);

free (cmd);

cmd = "end function;";

sc = SendData (con, cmd);

CloseConnect (con);

exit(0);

}

  1. Функция main() осуществляет установление соединения с сервером приложений и передачу набора данных с технологического процесса.
  2. Установка соединения с сервером приложений с помощью функции ConnectTo().
  3. Клиент с помощью функции SendCmd() посылает на сервер приложений команду о начале сессии, получает ответ.
  4. Далее клиент с помощью функции SendData() посылает данные, содержащие вызов функции инициализации технологического процесса. С помощью функции WaitData() клиент получает идентификатор сессии, требуемое количество и частоту значений технологического процесса.
  5. Функция rnd() используется для получения значений технологического процесса, которые клиент передает на сервер приложений с помощью функции SendData(), получая после каждой передачи данных результат их обработки.
  6. После этого с помощью функции SendCmd() клиент посылает команду на завершение сессии. А затем закрывает соединение.

ПРОГРАММА КЛИЕНТА НА FOXPRO

47. set libr to foxgen

ans=""

count = 0

delay = 0

flag = 0

  1. conid = connectto("as_server",1537)
  2. n=sendcmd(conid, "LI user1 paswd")

c=waitdata(conid,3,@a)

  1. n=sendcmd(conid, "BS")
  2. c=waitdata(conid,3,@a)

  3. n=senddata(conid, "function;")
  4. n=senddata(conid, "define s,c,d integer;")

    n=senddata(conid, "call Init(1) returning s,c,d;")

    n=senddata(conid, "send c,d;")

    c=waitdata(conid,3,@a)

    n=retrdata(a, @count)

    c=waitdata(conid,3,@a)

    n=retrdata(a, @delay)

    i = 0

    do while i < count

  5. rn = ndist(300,100)
  6. if rn < 0

    rn = 0

    endif

    if rn > 1000

    rn = 1000

    endif

    wait timeout delay/1000

    n=senddata(conid, "call NewData(s,"+str(rn)+");" )

    c=waitdata(conid,3,@a)

    n=retrdata(a, @flag)

    if flag = 1

    i = i+1

    endif

    enddo

  7. n=senddata(conid, "call UnReg(s);")

c=waitdata(conid,3,@a)

n=sendcmd(conid,"ES")

n=closeconn(conid)

  1. В начале программы осуществляется подключение библиотеки функций взаимодействия с сервером приложений.
  2. Установка соединения с сервером приложений с помощью функции CONNECTTO.
  3. Клиент с помощью функции SENDCMD посылает на сервер приложений команду о регистрации пользователя и получает ответ.
  4. Клиент с помощью функции SENDCMD посылает на сервер приложений команду о начале сессии и получает ответ.
  5. Далее клиент с помощью функции SENDDATA посылает данные, содержащие вызов функции инициализации технологического процесса. С помощью функции WAITDATA клиент получает идентификатор сессии, требуемое количество и частоту значений технологического процесса.
  6. Функция NDIST используется для получения значений технологического процесса, которые клиент передает на сервер приложений с помощью функции SENDDATA, получая после каждой передачи данных результат их обработки.
  7. После этого с помощью функции SENDDATA клиент посылает команду на завершение сессии. А затем закрывает соединение.

ОПИСАНИЕ ПРОГРАММ. ПРОГРАММА АДМИНИСТРАТОРА

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

  1. unit Main;
  2. interface

    uses

    SysUtils, Windows, Messages, Classes, Graphics, Controls,

    Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, Menus, Connect, ComCtrls;

    type

    TMainForm = class(TForm)

    RichEdit1: TRichEdit;

    Button1: TButton;

    Button2: TButton;

    Button3: TButton;

    Button4: TButton;

    Sock1: TConnect;

    procedure Button1Click(Sender: TObject);

    procedure Button2Click(Sender: TObject);

    procedure Button3Click(Sender: TObject);

    procedure Button4Click(Sender: TObject);

    procedure Connect1Error(Sender: TObject; str: String);

    procedure Connect1Reply(Sender: TObject; str: String);

    procedure Connect1Data(Sender: TObject; val: VALUE);

    end;

    var

    MainForm: TMainForm;

    stage, PackLen: integer;

    ch : string[1];

    st : string;

    implementation

    uses Unit3;

    {$R *.DFM}

  3. procedure TMainForm.Button1Click(Sender: TObject); //посылка команды
  4. begin //simple

    Connect1.SendData('CALL Servcmd(1,"Simple");');

    Connect1.SendData('CALL Server();');

    end;

  5. procedure TMainForm.Button2Click(Sender: TObject); //посылка команды
  6. begin //info

    Form3.Show;

    st:='CALL Servcmd(1,"info '+vvod+'");';

    Connect1.SendData(st);

    Connect1.SendData('CALL Server();');

    end;

  7. procedure TMainForm.Button3Click(Sender: TObject); //посылка команды
  8. begin //interval

    Form3.Show;

    st:='CALL Servcmd(1,"interval '+vvod+'");';

    Connect1.SendData(st);

    Connect1.SendData('CALL Server();');

    end;

  9. procedure TMainForm.Button4Click(Sender: TObject); //посылка сообщения
  10. begin //программе клиента

    Form3.Show;

    st:='CALL Servcmd('+vvod+',"Signal");';

    Connect1.SendData(st);

    Connect1.SendData('CALL Server();');

    end;

  11. procedure TMainForm.Connect1Error(Sender: TObject; str: String); //в
  12. begin //случае ошибки

    RichEdit1.SelAttributes.Color := clRed;

    RichEdit1.Lines.Add( str );

    end;

  13. procedure TMainForm.Connect1Reply(Sender: TObject; str: String); //прием
  14. begin //подтверждения выполнения команды

    RichEdit1.SelAttributes.Color := clBlue;

    RichEdit1.Lines.Add( str );

    end;

  15. procedure TMainForm.Connect1Data(Sender: TObject; val: VALUE); //прием
  16. var //данных

    s:string;

    begin

    RichEdit1.SelAttributes.Color := clBlue;

    if (val.null = 0) then

    case val.vtype of

    0: {AS_CHAR }s[1]:=sapis.val.c;

    1: {AS_INT }str (sapis.val.li,s);

    2: {AS_SMINT }str (sapis.val.si,s);

    3: {AS_DOUBLE}str (sapis.val.d,s);

    4: {AS_FLOAT }str (sapis.val.f,s);

    5: {AS_STRING}s:=sapis.val.p;

    end

    else

    case val.vtype of

    0: {AS_CHAR }s:='';

    1: {AS_INT }s:='0';

    2: {AS_SMINT }s:='0';

    3: {AS_DOUBLE}s:='0.0';

    4: {AS_FLOAT }s:='0.0';

    5: {AS_STRING}s:='';

    end;

    RichEdit1.Lines.Add( s );

    end;

    begin

  17. Connect1.HostName:=<имя компьютера>;
  18. Connect1.Connected := True;
  19. Connect1.SendCmd('LI <имя пользователя>,<пароль>);
  20. Connect1.SendCmd('BS');

Connect1.SendData('Function;');

Connect1.SendData('CALL Server();');

end.

  1. Модуль main осуществляет установление соединения с сервером приложений и передачу команд серверу приложений.
  2. Процедура TMainForm.Button1Click при нажатии соответствующей кнопки посылает серверу приложений команду Simple, используя функцию Servcmd().
  3. Процедура TMainForm.Button2Click при нажатии соответствующей кнопки посылает серверу приложений команду Info, используя функцию Servcmd().
  4. Процедура TMainForm.Button3Click при нажатии соответствующей кнопки посылает серверу приложений команду Interval, используя функцию Servcmd().
  5. Процедура TMainForm.Button4Click при нажатии соответствующей кнопки посылает программе клиента сообщение, используя функцию Servcmd().
  6. Процедура TMainForm.Connect1Error обрабатывает событие OnError и выдает сообщение об ошибке.
  7. Процедура TMainForm.Connect1Reply обрабатывает событие OnReply и выдает подтверждение выполнения команды.
  8. Процедура TMainForm.Connect1Data обрабатывает событие OnData и выводит поступившие данные.
  9. Задаем имя компьютера, на котором установлен сервер приложений.
  10. Устанавливаем соединение.
  11. Посылаем команду регистрации.
  12. Открываем сессию.

 

Список литературы

  1. Ладыженский Г.М. Разработка систем клиент-сервер. I. //СУБД. - 1996.- ©1.
  2. Ладыженский Г.М. Разработка систем клиент-сервер. II. //СУБД. - 1996.- ©2.
  3. Москаленко О.М. Опыт проектирования и разработки банковской системы для трехуровневой архитектуры клиент-сервер. //СУБД. -1996. -© 3.
  4. Девитт Д., Грэй Д. Параллельные системы баз данных: будущее высоко эффективных систем баз данных // СУБД .- 1995. - © 2.
  5. Коваленко В. Универсальный сервер приложений "Тарантелла". // Открытые cистемы. - 1998. - © 2.
  6. Ладыженский Г. М. Технология "клиент-сервер" и мониторы транзакций. // Открытые cистемы. - 1994. - © 3.
  7. Birney Ewan, Lausch Michael. ORBit Beginners Documentation,-http://icps.u-strasbg.fr/~genaud/ORBIT/ - 1998.