ЖУРНАЛ «СТА» №4/2006
Итак, копируем Device.x на компьютер с FreeBSD и снова запускаем rpcgen: $rpcgen –C /tmp/Device/Device.x Никаких сообщений нет, а в дирек- тории /tmp/Device появились файлы Device.h, Device_clnt.c, Device_svc.c, Device_xdr.c. Назначение этих файлов: ● Device.h – прототипы функций, ко- торые будет вызывать заглушка при получении запросов от клиента (их необходимо будет реализовать); ● Device_clnt.c – заглушка клиента (для реализации сервера не понадобится); ● Device_xdr.c – содержит функции, необходимые для преобразования заглушкой данных в соответствии со стандартом XDR; ● Device_svc.c – собственно код за- глушки, уже содержащий функцию main(). Дальнейшая задача построения кар- каса сервера сводится к созданию про- екта приложения QNX Neutrino на базе сгенерированных rpcgenфайлов и до- бавления файла с реализацией объяв- ленных в Device.h функций. Создание проекта приложения (организация ие- рархии директорий и генерация makeфайла) – достаточно тривиаль- ная процедура, она подробно описана в документации по QNX Momentics и в [6]. А в исходный код нужно внести не- которые изменения. 1. Добавим файл, содержащий реализа- цию функций каналов Core и Abort, назовём его Device_server.c (функции объявлены в Device.h). 2. Для того чтобы каркас сервера уда- лось скомпилировать и запустить, не- обходимо в реализации каждой функции, объявленной в Device.h, создать статический экземпляр структуры, возвращаемой этой функ- цией, присвоить полю error данной структуры значение 0 и вернуть адрес этой структуры. Например: ... Device_Error * device_abort_1_svc(Device_Link *argp, struct svc_req *rqstp){ static Device_Error result; result.error = 0; return &result; } Create_LinkResp * create_link_1_svc(Create_LinkParms *argp, struct svc_req *rqstp){ static Create_LinkResp result; result.error = 0; static &result; } ... 3. В файле Device_svc.c функцию main() необходимо привести к виду: ... int main (int argc, char **argv){ //Сетевой транспорт register SVCXPRT *transp; //Сброс кан. Abort pmap_unset (DEVICE_ASYNC, DEVICE_ASYNC_VERSION); //Сброс кан. Core pmap_unset (DEVICE_CORE, DEVICE_CORE_VERSION); //Создание транспорта transp=svctcp_create(RPC_ANYSOCK,0,0); if (transp == NULL ) { fprintf (stderr, "%s", "cannot create tcp service."); exit(1); } //Регистрация канала Abort if (!svc_register(transp, DEVICE_ASYNC, DEVICE_ASYNC_VERSION, device_async_1, IPPROTO_TCP)) { fprintf (stderr, "%s", "Portmap: unable to register (DEVICE_ASYNC)."); exit(1); } //Регистрация канала Core if (!svc_register(transp, DEVICE_CORE, DEVICE_CORE_VERSION, device_core_1, IPPROTO_TCP)) { fprintf (stderr, "%s", "Portmap: unable to register (DEVICE_CORE)."); exit(1); } //Запуск сервера в режим ожидания запросов svc_run (); fprintf (stderr, "%s\n", "svc_run returned"); exit (1); } Надо отметить, что по неизвестной причине идентификатор канала Abort именуется в спецификации RPCL [1] и, соответственно, в Device.h как DEVICE_ASYNC, а не DEVICE_ABORT, как следовало бы ожидать. Изменения, которые необходимо внести в main() из Device_svc.c (после отработки rpcgen), заключаются в уда- лении кода, отвечающего за создание транспортной службы UDP и регистра- цию каналов Core и Abort, доступных через эту службу (согласно VXI11 транспорт UDP не поддерживается). После того как написана некоторая реализация функции каналов Core и Abort и скорректирована функция main(), можно скомпилировать полу- ченный каркас в исполняемый модуль. На данном этапе будет полезно протес- тировать корректность реализации каркаса. Для этого запускаем на сторо- не прибора (QNX) portmap и модуль каркаса сервера. Воспользуемся снова утилитой NTRpcInfo на стороне кли- ента (Windows): С:\tmp>ntrpcinfo 192.168.10.81 Getting RPC information for: 192.168.10.81 ... ================================= Program: **DONE** Program ID : 395184 Version: 1 Port: 1022 Protocol: TCP ================================= Program: **DONE** Program ID : 395183 Version: 1 Port: 1022 Protocol: TCP Эксперимент показывает, что на сто- роне сервера дополнительно к про- грамме службы распределения портов появились программы с идентифика- торами 395183 и 395184, которые и представляют собой каналы VXI11. То есть каркас приборного сервера готов и функционирует в соответствии со спе- цификацией, теперь необходимо на- полнить каркас функциональностью. Реализация Core-интерфейса сервера Основная функциональность серве- ра VXI11 сосредоточена в канале Core. Всего в данном интерфейсе определе- но 15 функций (процедур). Начинается любое взаимодействие клиента с при- борным сервером с установления со- единения (connection), то есть с вызова процедуры create_link() . Рассмотрим реализацию данной функции более подробно. Входные параметры поступают в про- цедуру create_link() посредством структуры Create_LinkParms , а выходные возвращаются через Create_LinkResp: struct Create_LinkParms { //Идентификатор клиента long clientId; //Признак блокировки устройства //(0 – блокировать, 1 – нет) bool_t lockDevice; //Интервал ожидания (мс) возможности //заблокировать устройство u_long lock_timeout; //Наименование типа устройства char *device; }; struct Create_LinkResp { //Код ошибки П Р О Г РАММНО Е ОБ Е С П Е Ч Е НИ Е / ИН С Т Р УМЕ Н ТАЛ Ь НЫ Е СИС Т ЕМЫ 61 СТА 4/2006 www.cta.ru
Made with FlippingBook
RkJQdWJsaXNoZXIy MTQ4NjUy