本站首页链接

AVS公司英文网站

国际AVS中心

 

第六章 集成用户代码

CH6.zip(88KB)

在本章中,通过调用C程序创建一个组件。

在本章中你:

启动AVS/Express

创建EchoReader

保存EchoReader为一个模板

产生C代码

编译这个过程

测试EchoReader

6.1 介绍

你可以用两种基本的方式,把C,Fortran或C++代码集成到AVS/Express应用程序中:

创建一个模块——创建一个调用C、Fortran或C++程序来执行其处理过程的AVS/Express模块。

使用对象的实例——创建一个进入AVS/Express的C、Fortran或C++程序,以创建、清除和/或修改已有AVS/Express对象的实例。例如,这就允许你在服务器模式下建立一个调用AVS/Express的基于C的应用程序,来执行应用程序的某些方面,如可视化一组数据。

在本章中,创建一个调用C程序的AVS/Express模块。这个模块是Echo Sounding应用程序中的一部分,从一个文件中读echo-sounding数据。

AVS/Express为把C代码集成到一个模块中提供了几种技巧。在本章中,使用Add_Module工具增加模块中的方法和参数。

6.2 启动AVS/Express

如果你不在AVS/Express中,则现在用myproj项目来启动它。

打开一个命令外壳或一个xterm窗口。

浏览到express/myproj目录。

根据你存放myproj的地方,可能有必要指定另一个路径名。

键入下列平台特定的命令,启动AVS/Express。

可视化版用户键入:

UNIX

Windows

vxp –project myproj

bin\pc\vxp –project myproj

开发者版用户键入:

UNIX

Windows

express -project myproj

bin\pc\express –project myproj

AVS/Express开始并装入你的项目。

如果从上一章你就在AVS/Express里,那么删除当前的应用程序工作区,装入一个新的工作区。

选择File->Delete Application,删除当前的应用程序工作区。

选择File->New Application,装入一个新的应用程序工作区。

从新的应用程序对话中选择Application。

点击OK。

注意:你有C++编译器吗?

如果你没有C++编译器,你仍可以在本章中编译样本模块。(你将可以在一个名为用户的外部过程中执行它,你可以用C编译器建立这个名为用户的过程。)但是你必须用-nocxx命令行方式启动AVS/Express。在UNIX系统上用:express -project myproj –nocxx。

如果你需要这样做而你从上一章就在AVS/Express中,则退出并重新启动AVS/Express,这次指定-nocxx方式。

6.3 创建EchoReader

把EchoReader定义为一个模块,类似于Echo。尽管在本例中,EchoReader将通过调用一个C程序来执行它的处理过程——C程序是我们提供给你的echo.c。然而EchoReader能够读你指定的任何C程序。

EchoReader也需要一个输入参数,数据文件的名字。在这个指南中,我们也提供了这个文件,叫做echo.dat。echo.dat 和echo.c都可以在<install_dir>\getstart目录下找到。

echo.dat数据文件就象这样:

#header lines, each beginng with '#'

#

60

3828

0.0133 -71.1667 42.8333

0.0133 -71.1467 42.8333

0.0133 -71.1267 42.8333

第一个数值(60)是声源和接收器之间的距离,以英尺为单位。

第二个数值(3828)是文件中数据点的数值。

所有的子序列数值都是实际数据点。每一个数据点有三个组件:以秒计的时间(0.0133)、经度(-71.1667)和纬度(42.8333)。

相应地,EchoReader需要四个输出参数:DistanceSR,数据点的数值,时间和地点。

在Network编辑器中创建EchoReader是工作的开始。

启动Add_Module工具

创建一个在其中工作的ScratchPad。

选择File->New Application命令,从New Application对话中选择ScratchPad,并点击OK。

选择Object->Add_Module。出现Add_Module Wizard的首页。

点击“Object name”域,并用EchoReader替换NewModule。

你现在有一个模块EchoReader。下一步是增加一个方法。

增加方法

因为在本例中我们将增加C代码,所以点击“Type field”,并选择C函数(o method)来替代缺省的C++成员函数(cxx方法)。o-method指的是C代码。

点击Add按钮。

进入可以增加一个method对象的屏幕,method对象由亮栓图标标识。使用method对象连接EchoReader和一个C程序。

一旦在add_method页(下图)上点击Object名域,并键入“update”。这样就命名了你下面正在创建的方法。

注意出现method对象,并当Add_ Module工具工作时在Network编辑器中进行修改。

为更新的方法指定属性。在Method Type下选择Method runs when object is instanced。

Weight——确定赋给这个方法的优先级——应该被设置。

在C-function name域中敲入echo_reader。这将是更新调用函数的名字。

点击Done。你将返回Add_Module工具的首页。

点击Next。

创建和设置第一个参数

选择参数——在Type域中选择string菜单项。

点击Add按钮。

Add_Module工具显示参数的编辑器页。

给参数换名——在Object name域中,更换New Parameter为filename。

显示输入端口——置开关Object is an exported parameter和Display input port到on上。

注意:这些开关是互相独立的。当模块以Display Params模式被打开时,开关Object is an exported parameter决定这个参数是否是可见的,Display input port使这个参数的端口在模块接口处可得。你可以设置任何一个而不设置其它。

点击Next——进入增加参数过程的第二部分。

使用这一页设置属性,属性定义被选择的参数和你在上一面板中选择的方法之间的接口。在当前这个例子中,update是唯一的方法,所以自动被选择。

设置参数的method属性——对于filename,设置开关notify,read和req,设置文件名的这些属性。

Attributes是对象的布尔特性。当参数值变化时,notify属性说明当参数值变化时,应该告诉update方法。典型地,告知导致方法的执行。read属性表示这是一个输入参数。req属性表示这个参数是被要求的,意思是只有当这个参数有值时,update方法才能执行。更明确地,它意味着对象管理器将产生一个OMget.xxx调用,而不是OMset.xxx调用。

点击Done。返回add_parameter页,并允许你通过点击Next>退出或增加更多的参数。在这个例子中,我们增加更多的参数。

创建和设置其它的参数

为distance指定输出参数。

选择参数——在Type菜单中选择float。

点击Add,移动到add_parameter页。

给参数换名——在Object name域中,把NewParameter的名字改为distance_sr。

显示输出端口——将开关Object is an exported parameter和Display ouput port置到on上。

点击Next>移动到属性页。

对于distance_sr,设置开关write。

write属性表示对象是一个输出参数。

正如你已经看到的,当模块调用一个C程序时,你要指明哪些是输入数据参数,哪些是输出数据参数。输入参数filename得到read、notify和req属性。在本例中的所有输出参数——distance_sr、num_values、time和location——得到write属性。

选择Done。

为数据点的数值指明输出参数。

选择参数——在Type菜单中选择int。

点击Add,移动到add_parameter页。

给参数换名——在Object name域中,把NewParameter的名字改为num_values。

显示输出端口——置开关Object is an exported parameter到on上。

在EchoReader内,你只用num_values参数来设置你下一步指定的time和location数组的大小。这样你就不必为num_values显示一个到EchoReader外部对象的端口。这里我们只输出参数本身,以便所有的EchoReader参数总在Network编辑器中出现。

点击Next>移动到属性页。

设置开关write。

选择Done。

time值指明输出参数。

选择参数——在Type菜单中选择float。

点击Add,移动到add_parameter页。

给参数换名——在Object name域中,把NewParameter名改为time。

显示输出端口——置开关Object is an exported parameter和Display ouput port到on上。 设置参数维数-转变time参数到一个一维数组,数组的大小是num_values确定的。

点击开关Object is an array。

在Dimensions域中,在中括号之间键入num_values:

注意在Add_Module工具和Network编辑器中的time子对象的标题条,在对象名之后显示了一对中括号,表示对象是一个数组。

time参组需要一个足够大的数组容纳num_values元素, 这里num_values是数据点的个数。因为num_values是EchoReader的参数之一,所以time的大小依赖于num_values的当前值。如果num_values改变,那么time的大小也随之改变。

点击Next>移动到属性页。

设置开关write。

选择Done。

指明location值的输出参数。

选择参数——在Type菜单中选择float。

点击Add,移动到add_parameter页。

给参数换名——在Object name域中,把NewParameter名换为location。

显示输出端口——置开关Object is an exported parameter和Display ouput port到on上。

设置参数维数——在这个例子中,转变location参数到一个二维数组。

点击开关Object is an array。

在Dimensions域中,在中括号之间键入num_values,再键入一个左括号、2和一个右括号:

按Enter设置维数。注意location子对象的标题条显示两组中括号,表示一个二维数组。

象time参数一样,location参数需要一个足够大的数组容纳num_values元素。但是location数组还需要另外一维,存储每一个数据点的经度和纬度。

点击Next>移动到属性页。

设置开关write。

选择Done。

增加所有的参数和方法到新模块EchoReader中。

点击Next>进入Add Module过程的下一步。

设置与集成一个C程序有关的特性

对于调用C程序的模块,需要提供确定的与代码相关的信息。对于EchoReader,设置模块和它的参数的特性和属性,来指明:

C程序的名字(已设置)

C程序的源文件和目录

C程序的处理过程(正如你将看到的,因为你想要EchoReader在用户进程中执行,所以你要指定一个处理过程,但是要把EchoReader放置在设置缺省进程的库中表示。)

在参数和方法之间的接口(例如,当参数变化时,方法应该被通知。)(已经设置)

哪些数据参数是输入参数,哪些数据参数是输出参数(已经设置)

通过一组代码管理特性,你提供信息给AVS/Express。你可以使用Add Module过程设置这些特性。

注意:你可能已经在对象管理编辑器中设置了代码管理特性、属性、以及参数和方法特性。在Add_Module工具中,我们一次对它们的全部属性进行设置,这样你就能知道你并没有遗漏任何事情。

对于EchoReader,我们打算给“user”设置过程特性,给“echo” 设置build_dir特性,和给“echo.c”设置src_file特性。这些都在Add_Module过程的第3步已经执行了。

设置过程给用户——从选项菜单Process in which object resides中,选择user选项。

process特性指明了这个程序的过程。

键入源文件名——在Source Filename域中,键入echo.c。

这个域指定了源文件的名字,为模块设置src_file特性。

设置建立的目录——在Advanced Properties面板上,给echo设置build_dir域。

build_dir特性指明了源文件的目录。你指定相对于项目目录的目录。当前的项目目录显示为Source文件名域上面的Directory for source。你在build_dir中指定的建立目录被添加到这个路径上。

这个域为模块设置build_dir特性。

点击Done。这样会完成模块,退出Add-Module工具。

注意:非常重要:叫作“echo.c”的文件还不在你的源目录中。要使这个例子运转,你将需要从<install_dir>\getstart\目录将echo.c拷贝到<install_dir>\myproj\echo\上。除非你这样做,否则例子不会执行。然而在你这样做之前,继续阅读下面两节。怎样设置echo.c文件的说明可以在6~19页的修改echo.c一节中找到。

6.4 保存EchoReader为一个模板

在本节中,保存EchoReader为一个模板,并保存你的项目。

关闭EchoReader。

进入Library Workspaces页。

从ScratchPad工作区拖拉EchoReader到Workspace 1中。

保存你的项目。

选择Project->Save。

选择File->Delete Application,删除ScratchPad。

删除ScratchPad显露你前面的应用程序工作区,尽管在Applications中它当前处于关闭状态。

6.5 产生C代码

你可以从暂存中为EchoReader写C代码。或者你可以允许AVS/Express产生代码的一个框架版本,这个框架版本是在之后你可以修改的。在本节中,你用到产生和修改技术。

产生代码

你可以从AVS/Express中编辑一个模块的源文件。如果你请求这样做,而源文件还存在,那么AVS/Express就产生这个源文件的框架版本,这个框架版本是在之后你可以修改的。

在Workspace 1库中选择EchoReader。不要实例化它——只点击它。

AVS/Express用蓝色加亮EchoReader。

选择Project->Edit Source。

AVS/Express尝试为EchoReader的源文件打开一个编辑器。AVS/Express在项目目录下寻找echo目录和echo.c源文件。这是你为EchoReader指定的。但是echo和echo.c并不存在,所以在用一个弹出菜单提示你之后,AVS/Express创建它们和代码的一个框架版本。AVS/Express然后在编辑器中打开echo.c。(在UNIX系统中,是由EDITOR环境变量来指定编辑器。在Windows中,它是你的缺省编辑器——通常是Visual Studio。)

这是echo.c的顶端部分:

#include "user.h"

int

echo_reader(OMobj_id EchoReader_id,

OMevent_mask event_mask,

int seq_num)

{

/***********************/

/* Declare variables */

/***********************/

char *filename = NULL;

double distance_sr;

int num_values;

int time_size;

float *time;

int location_size;

floit *location;

/***********************/

/* Get input values */

/***********************/

/* Get filename's value */

if (OMget_name_str_val(EchoReader_id,

OMstr_to_name("filename"),

&filename, 0) != 1)

如果你滚动文件,你会看见它包括几个对象管理器API调用,例如OMget_name_str_val和OMset_name_real_val。类似这样的调用获得模块的输入参数,设置模块的标量输出参数,并得到模块的数组参数的指针。(你很快会看到一个有细节注释的程序版本。)

编辑EchoReader的定义

或者现在或者在后面你编译过程时,你会发现用你定义EchoReader的方式有些不正确。例如,如果你忘记设置update对象给目标程序名,AVS/Express将不会为你产生一个程序。

如果不管什么原因你需要编辑EchoReader的定义,按下面的指示做。即使EchoReader的定义是正确的,你可能还想现在就练习这些步骤。

从编辑器中退出。

选择File->New Application和从New Application对话中选择ScratchPad,装入一个ScratchPad应用程序工作区。

从Workspace 1实例化EchoReader到ScratchPad工作区中。

对EchoReader做所需的改变。

如果你只是练习可以跳过这一步。

拖拉EchoReader回到Workspace 1。

保存你的项目。

选择File->Delete Application删除ScratchPad。

双击在DefaultApplication上或选择最大化弹出命令,最大化DefaultApplication工作区。

修改echo.c

初步地通过增加代码去阅读数据文件和集中输出参数,你现在可以修改所产生的代码。

为了节省你的时间,echo.c的一个修改后的版本在express目录下得到。你可以拷贝这个版本,而不用作任何修改。

如果你仍然在包含echo.c的已产生版本的编辑器中,那么退出它。

从express目录拷贝echo.c到myproj的echo目录下。

UNIX系统——在一个xterm窗口中,插入:

cp install_dir/getstart/echo.c-/myproj/echo/

Windows——在一个DOS命令外壳中,进行下列插入,或在文件管理器中做相应的工作:

copy install_dir\getstart\echo.c path\myproj\echo\

install_dir是你安装AVS/Express的目录。

查阅echo.c

这是echo.c的修改代码,包括细节注释说明它是怎样工作的:

#include "user.h"

#include <stdio.h>

/********************************************************/

/*echo-reader: reads a data file into the echo-reader module. */

/* */

/*Most of the code you see here was generated by avs/Express. */

/* This include the function declaration, the OM calls */

/* to access the module's data parameters, and the variable*/

/* definitions related to those calls. */

/*You add routine specific code, in this case, code to open */

/* a data file, read its values, and populate the time and */

/*location arrays. */

/***********************************************/

/*AVS/Express generates the function declaratin you see*/

/* below. The function's first input parameter is the key*/

/* one for the purposes of most routines. AVS/Express assigns */

/*a unique object ID to each object in the object hierarchy.*/

/* The first input parameter contains the object ID of the*/

/*calling module. From this ID, you can access the module's*/

/* additional information, which you do not need for this module.*/

/****************************************************/

int echo_reader(OMobj_id echo_reader_id,

OMevent_mask event_mask, int seq_num){

/*************************/

/* Declare variables */

/************************/

char * filename = NULL;

double distance_sr;

int num_values;

int time_size;

float *time;

int location_size;

float *location;

int i;

char Buff[512];

FILE *fp;

/*************************************************/

/* A routine typically begins by getting the module's input */

/* data parameters. In this case, it gets the value of */

/* parameter filename. */

/* */

/* Most API calls have the prefix OM, for Object Manager.*/

/* The call to OMget_name str_val, generated for you by */

/* AVS/Express, looks in the module identified by */

/* echo_reader_id for a parameter named "filename", and */

/* places the value of that parameter in the variable */

/* filename. */

/***********************************************/

if( OMget_name_str_val(echo_reader_id,

OMstr_to_name("filename"),&filename,0) != 1)

filename = NULL;

/****************/

/* Fucntion's Body*/

/***************/

/* Code you supply to open the data file. */

fp = fopen(filename,"r");

if (fp == NULL){

fprintf(stderr, "cannot open file %s\n",filename);

return(0);

}

/* Code you supply to strip off header (lines beginning with #) */

/* (assumes there is a blank line after last header line) */

while (fgets(Buff,sizeof(Buff),fp))

if (Buff[0] != '#') break;

/* Code you supply to read distance SR and the number of*/

/* data points. These are the first two values in the*/

/* input file.*/

fscanf(fp,"%lf",&distance_sr);

fscanf(fp,"%d",&num_values);

/***********************************************/

/* AVS/Express also generates OM calls to set the value of*/

/* the module's scalar output parameters. For example, */

/* the first call to OMset_name_real_val looks in the*/

/* module identified by echo_reader_id for a parameter*/

/* named "distance_sr" and places into that parameter the*/

/* value of distance_sr.*/

/**********************************************/

/* Set distance_sr's value */

OMset_name_real_val(echo_reader_id,

OMstr_to_name("distance_sr"),distance_sr);

/* Set num_value's value*/

OMset_name_int_val(echo_reader_id,

OMstr_to_name("num_values"),num_values);

/*************************************************/

/* For array parameters, AVS/Express generates OM calls to*/

/* return a pointer to the array. For example, the first */

/* call to OMret_name_array_ptr looks in the module */

/* identified by echo_reader_id for a subobject named */

/* "time" and returns a pointer to the array. The argument */

/* OM_GET_ARRAY_WR tells the object Manager that you intend*/

/* to write to the array. After the call, the fourth */

/* argument, time_size, contains the number of elements in */

/* the array. */

/*************************************************/

/* Get a pointer to the time array */

time = (float*) OMret_nam_array_ptr(echo_reader_id,

OMstr_to_name("time"),OM_GET_ARRAY_WR,&time_size,NULL);

/* Get a pointer to the location array*/

location = (float*)OMret_name_array_ptr(echo_reader_id,

OMstr_to_name("location"),OM_GET_ARRAY_WR,&location_size,NULL);

/* Code you supply to read the data into the arrays*/

for(i=0;i<num_values;i++)

fscanf(fp,"%f %f %f",time+i,location+2*i;location+2*i+1);

/************************************************/

/* The Object Manager keeps track of array references. A*/

/* call to OMret_name_array_ptr tells the Object Manager*/

/* that the routine needs to reference the array. A */

/* subsequent call to ARRfree, generated by AVS/Express,*/

/* tell the Object Manager that the routine is finished */

/* with the array. */

/***********************************************/

if (filename) free(filename);

if(time != NULL) ARRfree((char*)time);

if(location != NULL) ARRfree((char*)location);

return(1);

};

6.6 编译这个过程

已经建立了放置EchoReader的库Workspace 1,这样在其中已经编译的模块通过缺省属于express过程。但是当你在AVS/Express中时,不能编译express过程,因为express过程正在运行。所以如果你想要在AVS/Express中编译EchoReader,你需要指定EchoReader属于一个外部过程。AVS/Express提供了一个外部过程,叫做user。

在前一步,你为“user”设置了EchoReader的过程属性,因此跳过了库的缺省。现在准备编译这个user过程。

在Workspace 1库中,选择EchoReader。

AVS/Express用蓝色加亮EchoReader。

选择一个模板对象表示你想要编译的过程。AVS/Express编译由process特性为所选对象所指定这个过程。你可能已经选择了属于user过程的任意一个对象。

选择Project->Compile。

AVS/Express编译user过程。这包括产生一个makefile和连接这个过程。出现一个窗口显示通知信息和可能产生的任何错误信息。

在一个成功编译的结尾,出现下列信息:

Hit any key to continue...

如果过程编译没有错误信息,通过指向它和按Return键或任意其它键来清除这个窗口。

如果有错误,再次编辑echo.c和编译这个过程。

6.7 测试EchoReader

在第8章创建和编译应用程序中,你将与在一个实际应用程序中的Echo一起使用EchoReader,这个实际应用程序包括一个用户接口和一个数据视图。

现在,在EchoReader上简单地测试它自身。你可以把它作为在express安装目录下的一个样本数据文件的输入来使用。

实例化EchoReader到DefaultApplication工作区上。

如果Default Application窗口没有最大化,那么在其上双击或选择最大化弹出命令来最大化它。

打开EchoReader,拓宽它,打开文件名。

插入数据文件名。

文件叫echo.dat,位于install_dir/getstart中。

例如,如果安装目录是/home/express,你应插入下列内容。记住用引号包住字符串。

关闭文件名。

关闭对象应用赋值。

EchoReader的更新方法被告知文件名已经更改,所以它执行。这个程序读取数据文件并将数据放置到输出参数中。

这些参数的标题条表示它们现在有值:

打开time。

出现一个包括time的值的可滚动的文本窗口。

关闭time,打开location。

出现一个包括location的值的可滚动的文本窗口。

这一节结束。

或者在AVS/Express中继续下面的指南,或者选择File->Exit退出AVS/Express。

6.8 更多的信息

参见《使用AVS/Express》手册的第9章“使用模块”和第10章“应用程序接口”。

    

 

地址:北京市海淀区上地佳园6号楼1单元501室(100085)

电话:010-62971354,010-62970626 传真:010-62970626

E-mail:avs@visualsky.com Rockies@public.bta.net.cn