周小娟
(西安外事学院 计算机中心,陕西 西安 710077)
Matlab是一款优秀的,功能强大的科学计算软件,它为航空航天,控制,机械,电子,电力,生物,财务等众多领域提供功能强大的数值计算和仿真工具。Matlab环境下的M语音,简单灵活,使用方便,但做为一种编程语言,在图形界面开发方面和Java,C++等高级语音相比却显得不够强大。Matlab虽然提供了一些图形开发功能,并且利用这些功能开发相对简单的图形界面也很受欢迎,但对于稍微复杂一些的图形界面和大型专业的软件产品,这些功能就无法实现了。因此在大型专业软件产品开发中,必须研究一种新的开发方法,将Java,C++等高级语言的图形界面开发优势和Matlab数据计算和仿真的优势结合起来,开发更为专业的软件产品。幸运的是,Matlab已经提供了和Java,C,C++等高级开发语言的接口,因此将Matlab程序整合进Java,C,C++程序是可行的。
Matlab提供将M程序编译为C/C++动态库或者Java包的功能。 在Deployment Tool中用户可以选择要编译的M文件以及编译结果的类型,然后Deployment Tool将调用编译器mcc(MATLAB COMPILER COMMAND)对目标文件进行编译。mcc具有将M文件编译为C/C++动态库,Java包,可执行文件的能力,是Matlab和其它语言混合编程中必须使用的一个工具。Matlab还提供另外一个工具mbuild将C/C++程序编译为可执行文件或者动态库,mbuild内部将调用操作系统已经安装的编译器进行编译。
Java语言具有优秀的图形界面开发能力,常用的图形开发库有基于 AWT(Abstract Window Toolkit)的 Java-Swing 开发技术和基于 SWT(Standard Widget Toolkit)的 Eclipse开发技术。AWT具有和Java类似的和操作系统无关的特征,因此在Windows上开发的AWT程序,在Linux上运行具有相同的界面风格。SWT使用操作系统提供的图形界面,和程序运行的操作系统具有直接关系,因此在Windows上开发的SWT程序,具有Windows操作系统的风格,在Linux上运行时具有Linux操作系统的风格。因为SWT直接调用的是操作系统本身的图形库,因此SWT可以提供完全的图形界面,功能比AWT要强大的多。本文将使用SWT在W indowsXP操作系统上进行Java程序图形界面的开发。
在Java中调用Matlab程序有两种方法:1.将Matlab程序编译为Java包,然后在Java程序中调用Java包中的类和方法;2.将Matlab程序编译为C/C++动态库,然后通过JNI(Java Native Interface)在Java程序中调用。第一种方法的实施比较简单,直接在Matlab中调用Deployment Tool就可以完成;第二种方法的实施比较复杂一些,需要编写Java Native类和C/C++代码,但在执行效率方面具有优势。因此在这两种方法的选择上以程序的规模和效率为参考进行,如果Matlab程序的规模比较小,处理的数据量比较小,可以选择第一种方法,反之选择第二种方法。对于执行效率比较重要的程序来说,可以用两种方法来实现混合编程,然后通过对比选择效率高的一种方法进行实施。下面对这两种方法的实现过程分别进行描述。
将Matlab程序编译为Java包,然后在Java程序中调用Java包中的类和方法,这种混合编程方法相对比较简单。具体步骤如下:
1)在Matlab命令行输入deploytool启动Deployment Project对话框。
2)在对话框中输入编译项目名称及其位置,并选择Java Package为编译类型。
图1 创建编译项目Fig.1 Create a deployment project
3)在Java Package页面中,通过添加类为要生成的Java类命名,然后通过添加文件为这个类添加相关的M文件。
图2 为编译项目命名要创建的Java类并添加M文件Fig.2 Add Java class and M files for the deployment project
4)点击Java Package页面右上方的Build按钮进行编译。
编译完成后,在编译项目所在目录将生成一个以编译项目名称命名的目录,在这个目录内有两个子目录distrib和src。distrib子目录放置编译好的Java包和帮助文档,这些都是我们期望的结果,其中Java包就是混合编程中Java一方需要用到的唯一文件。src子目录内放置的是Java包中类的代码文件,这些文件是中间产物,混合编程中Java一方不需要这些文件。因此一般不需要关心src子目录中的产物。
混合编程中在Matlab一方编译生成Java包后,Matlab的编程任务就完成了。下面需要在混合编程中Java一方调用该Java包中的类和方法。具体的调用方法如下,
1)将Matlab生成的Java包包含在Java项目的classpath中。这就保证了Java包中的类在当前Java项目内可以访问到;
2)在调用Matlab算法的Java类文件中导入com.mathworks.toolbox.javabuilder.*和MATLAB编译生成的Java类;
import com.mathworks.toolbox.javabuilder.*;
import rankine_package.*;
3)在调用Matlab算法的Java类文件就可以直接使用MATLAB编译生成的类了;
rankineClass MLRankine=new rankineClass();
Object[]Output=MLRankine.rankine(1, Input);
这个例子中,rankineClass就是在Matlab中编译生成的Java类,其方法rankine()就是M文件rankine.m中实现的Matlab算法,第一个参数的值1表示这个算法的返回值只有一个。
将Matlab程序编译为C/C++动态库,然后在C/C++程序中调用该动态库生成另外一个动态库,最后通过JNI(Java Native Interface)在Java程序中调用Matlab程序中的函数对应的C/C++函数,这种混合编程方法相对比较复杂。具体步骤如下:
1)首先必须选择并设置Matlab中mcc和mbuild在编译时使用的C/C++编译器。通过在Matlab中输入 “mbuild–setup”,Matlab会把当前操作系统已经安装的C/C++编译器列出来,这时候只需要选择要使用的一个编译器就可以了。如果当前系统没有安装任何编译器,需要安装一个。在Linux操作系统上,可以安装gcc。在Windows操作系统上,可以安装Microsoft Visual Studio for C/C++。
2)在设置好编译器后,就可以启动deployment tool将要使用的M文件编译为C/C++动态库了。编译过程和2.1节一样,只不过编译类型需要选择C动态库,或者C++动态库。编译结果中distrib目录中有4个结果文件在后续的步骤中要用到:一个*.dll文件,这是编译的结果动态库包含MATLAB程序对应的函数;一个*.h文件,动态库中函数的列表,在调用动态库的程序中需要用到;一个*.lib文件,动态库对应的静态库;一个*.exports文件,罗列了所有*.h中的函数名称。在*.h文件中,每一个Matlab程序的函数都有一个对应的C/C++函数,除此之外,还有两个函数也必须被用到,它们是xxxInitialize()和 xxxTerminate(),其中 xxx 是编译项目的名称。xxxInitialize()必须在调用任何Matlab函数对应的C/C++函数之前调用,xxxTerminate()必须在所有调用完成后,程序结束前调用。
3)在编译完 Matlab程序后,就可以进行 JNI(Java Native Interface)程序的开发了,通过JNI程序实现在Java图形界面程序中调用C/C++动态库。
①首先必须在Java图形界面程序中声明一个Java类包含若干native方法。这些native方法中,必须有两个native方法分别和 xxxInitialize()和 xxxTerminate()对应,其它的 native方法分别和*.h中要被用到的函数对应。下面就是一个这样的类声明的例子,
在这个例子中,通过System.load()函数装载的C/C++动态库libjnirankine.dll就是JNI要生成的动态库。这个动态库实现了这个Java类中的所有native函数,而这些native函数分别调用了Matlab程序编译结果动态库中的函数。
图3 一个包含native函数的Java类的例子Fig.3 A Java class example with native methods
②通过javac命令将这个Java类进行编译生成jniRankine.class,然后调用javah–jni jniRankine生成一个*.h文件jniRankine.h。javah工具将所有native函数转化为了JNI中的C/C++函数。这时候需要开发者手动生成一个jniRankine.c或者jniRankine.cpp来实现jniRankine.h中的所有函数。jniRankine.c或者jniRankine.cpp必须包含Matlab程序编译结果中的*.h文件,然后在jniRankine.c或者jniRankine.cpp中的各个函数中调用对应的Matlab程序编译结果中的C/C++函数。这样就完成了JNI的编程。最后通过在Matlab中运行如下mbuild命令编译jniRankine.h以及jniRankine.c或者jniRankine.cpp,并链接Matlab程序编译结果库,生成 libjnirankine.dll,
mbuild-I“C:Program FilesJavajdk1.7.0_05include” -I“C:Program FilesJavajdk1.7.0_05includewin32” jnirankine.cpp rankineDll.exports rankineDll.lib
-link shared-output libjnirankine
在这个命令中I参数后面是JDK的安装路径,rankine Dll.exports和rankineDll.lib是Matlab程序编译的结果,libjnirankine是编译结果。
③最后在Java图形界面程序中就可以像使用一般的Java类一样调用jniRankine类中的方法进而应用Matlab程序中的算法了。必须保证Matlab编译结果中的*.dll文件在java.library.path中可以找到。
文中使用第1章中描述的两种方法分别实现了计算理想朗肯循环的热力学性质的程序,程序比较简单,方便读者明白程序结构和流程。程序的开发环境为Eclipse和Matlab。在开发机器上安装了JDK1.7.0_05来支持Eclipse环境和第一种方法中的Java包的编译;在开发机器上还安装了Microsoft Visual Studio10.0 for C++来支持第二种方法中C++程序的编译。
图4 理想朗肯循环的热力学性质计算程序的图形界面Fig.4 GUI of thermodynamic efficiency of Ideal Rankine Cycle calculating program
程序的界面通过使用SWT图形开发技术开发,如图4所示,在WindowsXP操作系统上运行时,SWT程序界面风格和WindowsXP保持一致。在窗口的左边部分是计算时的输入参数,“Load Defaults”按钮会自动给各个输入参数填充默认值。点击 “Calculate”按钮程序就会调用Matlab程序的算法进行热力学性质的计算,并将计算结果显示在窗口的右半部分的输入框中。
Matlab的基本构成和功能决定了Java-Matlab混合编程的可行性,利用这种混合开发技术可以开发出功能强大,界面友好,具有良好扩展性和维护性的模块化的科学计算程序。这种混合开发技术可以使得进行图形界面开发的Java程序员和进行数值运算开发的Matlab程序员分离开来,彼此专注于自己擅长的方面,降低的开发难度,提高了开发效率。
[1]Hanselman D,Littlefield B.精通MATLAB7[M].北京:清华大学出版社,2006.
[2]李传军.C语言与MATLAB接口--编程与实例[M].北京:北京邮电大学出版社,2004.
[3]罗星月.Matlab与C语言程序的应用编程接口[EB/OL].(2007).http://www.builder.com.cn/2007/1030/591033.shtml.
[4]Muller Held.JMatLink[EB/OL].(2006).www.held-muellerde/JMatLink/.
[5]The Mathworks,Inc.Matlab帮助文档:Matlab builder for Java[S].2007
[6]廖云伢,王建新,盛羽.基于Java与Matlab集成的虚拟实验平台的设计与实现[J].计算机应用,2007,27(2):394-399.LIAO Yun-ya,WANG Jian-xin,SHENG Yu.Design and implementation of virtual lab platform based on integration of java and matlab[J].Journal of Computer Applications,2007,27(2):394-399.
[7]张崇明,汪春梅,朱品昌,等.使用Java扩展Matlab[J].计算机工程与设计,2006,27(9):1563-1565.ZHANG Chong-ming,WANG Chun-mei,ZHU Pin-chang,et al.Extending matlab using java[J].Computer Engineering and Design,2006,27(9):1563-1565.