第六章 集成用户代码
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章“应用程序接口”。

|