Java 调用matlab 函数接口

 

Java调用matlab函数接口有两种方式:

一种是通过matlab把函数打成jar包;

一种是把matlab编译成dll后,用C++再封装成java能支持的数据类型的dll

注意:不论用这两种方式中的哪一种,最终部署时都需要matlab环境(MCR,在matlab安装路径下有)。

1. 方式一:matlab直接打jar

1.1. 利用matlab自带工具打jar

1.1.1. 建立jar

matlabCommond Window中输入deploytool回车,则会出现下图:

 

 java调用matlab函数接口的两种方案_第1张图片

Name即给jar包起个包名,建议填写与项目名称有关系的名称;Location是打jar包的工程放到哪里;Target选择Java Package,如下图:


 java调用matlab函数接口的两种方案_第2张图片

 

点击OK后会多出如下子窗口,则第一步工作完成。


java调用matlab函数接口的两种方案_第3张图片

 

1.1.2. jar包中添加类

Java是面向对象的,所以要封装数据接口得先有个类。

点击Deployment Tool窗口中的Add Class,填写相应的类名,建议填写模块名称,如图:


 java调用matlab函数接口的两种方案_第4张图片

1.1.3. 向类中添加函数接口

假设一个函数接口写在一个m文件中,比如estimate_yaw.m中封装了函数:

function [status] = estimate_yaw(station_id, turbine_id, year_num, month_num)

% 简介: 计算指定风机在某段时间的偏航系统评分

% 输入: station_id 场站编号,数值类型

%   turbine_id 风机编号,数值类型

%   year_num 用于计算偏航评分的数据的年份,数值类型

%   month_num 用于计算偏航评分的数据的月份,数值类型

% 输出: status 程序运行状态

end

 

则点击Deployment Tool窗口中的YawScore的类下面的Add Files,选择要添加的数据接口对应的m文件,比如这里是estimate_yaw.m,如图即为添加接口后的效果:


 java调用matlab函数接口的两种方案_第5张图片

1.1.4. 编译

点击Deployment Tool窗口的编译按钮接口开始编译jar包,如图:


 java调用matlab函数接口的两种方案_第6张图片


 java调用matlab函数接口的两种方案_第7张图片

 

编译完成后到第一步中填写的Location中的路径下会有个与jar包包名相同的文件夹,其中包含distribsrc子文件夹,而jar包就在src文件夹下。

注:如果编译失败,报找不到javac命令,意味着需要java环境。下载并安装jdk,配置好JAVA_HOMECLASSPATH环境变量后,需要重启matlab

1.2. Java调用jar

Java调用matlab编译的jar包还需要javabuilder.jar包做支持,在$(MATLABPATH)\ toolbox\javabuilder\jar\文件夹下。

Java在调用jar包中的接口时,第一个参数表示接口的返回参数个数,后面才是matlab函数中的参数。另外,要注意数据类型对应,全部数据类型包括:


 java调用matlab函数接口的两种方案_第8张图片

详细的使用方法参见matlab的帮助文档中的MATLAB Builder JA分支,如图:

 java调用matlab函数接口的两种方案_第9张图片

 

2. 方式二:通过C++封装matlab编译的dll

2.1. matlab编译动态库

64位电脑上安装的matlab一般是64位的,那么其matlab编译器也是64位的,根据Matlab帮助文档的提示,要想生成32位的动态库,必须要有32位的Matlab编译器,且编译时要在DOS中编译,增加参数-win32即可。如果系统本身就是32位的就不用考虑这些了。

如果第一次使用Matlab编译动态库,要先做准备工作:mex -setup 以及mbuild

Matlab编译动态库命令:mcc 

ü 方式一:mcc -l model_hhl.m

ü 方式二:mcc -W cpplib:libModelHHL -T link:lib model_hhl.m

其中方式二中的libModelHHL 表示生成的lib文件的名称。两种方式的区别是:方式一生成的动态库数据接口类型是mxArry,是C中的结构体,使用时需要注意内存管理;方式二生成的动态库数据接口类型是mwArry,是C++中的类,使用时走的是面向对象思想,内存会随着析构函数而释放。如果不能很好的操作内存,一般建议用第二种。

编译完成后,在m文件所在目录下产生一堆文件中,需要的文件有三个:.h.dll.lib三个文件。三个文件的作用:.h是头文件,向程序员说明数据接口是如何定义、如何调用的;.lib是库文件,相当于索引,告诉机器到哪个位置去找哪个函数或者哪个类(函数和类都是dll导出的);.dll是二进制可执行文件,用来执行你封装的程序的。

PS:m文件编译为不依赖环境的exe方法:mcc -m program.m

2.2. C++封装动态库

Matlab编译出dll后,把.h.lib.dll三个文件拷贝出来待用。

Windows下开发C++项目,一般选用VS/VC集成开发环境,配置环境相对便捷,其代码补全的功能也令许多开发人员爱不释手,所以这里选择使用VS开发封装dllC++项目。

2.2.1. 创建C++ dll工程并导出接口函数

VS中新建项目时选择win32项目,如图:

 java调用matlab函数接口的两种方案_第10张图片

注:如果想要编写62dll的话也是选择win32项目,代码编写完以后配置编译选项就可以了。

确定后,在win32应用程序向导中选择应用程序类型为DLL,附件选项选择导出符号即可。


 java调用matlab函数接口的两种方案_第11张图片


完成后系统会自动生成一个dll工程,里面有几个导出dll的例子,包括导出类,导出函数,导出变量,如下:

 

java调用matlab函数接口的两种方案_第12张图片

java调用matlab函数接口的两种方案_第13张图片

java调用matlab函数接口的两种方案_第14张图片



为了便于java调用,建议导出函数时使用extern "C" LIBMATLABFUN_API而不是extern LIBMATLABFUN_API。后者导出的函数的函数名称会被加入一些奇怪的符号,具体原因请参考《深度探索C++对象模型》。

2.2.2. C++开发环境——VS如何配置环境

创建工程后首先要配置好环境,保证你的程序能知道:

到×××你需要调用的matlab编译出的dll对应的lib文件;

你需要调用的matlab编译出的dll对应的lib文件叫什么名字(根据这个lib文件就可以找到dll模块);

全部的系统默认的路径在属性页中的VC++目录中可以查看,如果.h.lib文件不在这些路径下,则要另外配置。那么要配置什么呢?其实很简单!告诉VS到×××头文件(.h),到×××库文件(.lib)就行了。

ü 头文件路径配置

选中项目,右键菜单中选择属性 -> C/C++ -> 常规 -> 附件包含目录

 java调用matlab函数接口的两种方案_第15张图片

ü 库文件路径配置

选中项目,右键菜单中选择属性 -> 链接器 -> 常规 -> 附加库目录

 java调用matlab函数接口的两种方案_第16张图片

选中项目,右键菜单中选择属性 -> 链接器 -> 输入 -> 附加依赖项(把要用的库文件名字加上,比如libModelHHL.lib,不用带路径)

 java调用matlab函数接口的两种方案_第17张图片

 

配置路径的时候可以使用绝对路径,也可以使用相对路径(用一个点表示当前目录,用两个点表示当前目录的上一级,比如.\include\表示当前目录下的include目录)。为了便于工程拷贝到其他电脑时配置环境方便,建议用相对路径,但是一定要弄清楚当前工作目录是什么(创建项目的时候,选择是否创建解决方案目录对工作路径是有影响的,一般可以通过项目自动生成的.h或者.cpp文件判断当前路径是什么)。

为了便于C++项目的维护,一般都会把头文件专门放在.\include\中,把.lib文件放到.\lib\中,把.dll文件以及它所依赖的dll都放到当前项目生成的exe或者dll的目录下。

另外,如果配置环境的时候用到系统环境变量,则要注意两点:一个是,配置系统环境变量的时候不要加分号,否则在VS里面用系统环境变量时会造成路径分割,比如MatlabPath变量配的时候是D:\Program Files\MATLAB\R2010b\;那么VS里面$(MatlabPath)\extern\include\会变成D:\Program Files\MATLAB\R2010b\\extern\include\两个路径;另一个是设置了系统环境变量后要重启电脑,否则会出现问题;PS:如果设置了MatlabPath后,启动Matlab出现??? Undefined function or variable 'matlabrc'错误提示,删除MatlabPathmatlab就可以正常运行了。如果嫌麻烦,就把要用到的.h.lib.dll文件拷贝到工程目录下后配置下环境。

配置环境的时候要注意配的是DEBUG还是RELEASE,是win32还是X64。一般配环境出现的问题无外乎以下三种:

找不到xxx.h文件

找不到xxx.lib文件

无法解析XXX 命令

找不到头文件说明头文件配置没配置好,检查一下附加包含目录是否正确,或者头文件是否存在;找不到lib文件说明库文件没配置好,检查一下附加库目录对不对,或者lib文件是否存在;无法解析XXX命令多半是没有配置lib文件,或者dll文件的问题,可能是dll文件找不到,可能是dll和对应的lib不是同一个版本,可能是与调用dll的程序平台不一样(32位或64位),一般从这几个方向入手就可以找到问题所在。

 

使用C++封装matlab生成的库时,配置好环境时需要拷贝的头文件如下:



java调用matlab函数接口的两种方案_第18张图片


配置好环境后,在代码中要与matlab生成的dll接×××互,需要包含的头文件:

#include "matrix.h"

#include "mclmcr.h"

注意,包含这两个头文件时,必须晚于包含matlab编译dll时生成的头文件,否则会报错,不能识别mclInitializeApplication函数。

需要加载的库:

mclmcrrt.lib

mclmcr.lib

如果环境配置好以后,报错说无法解析xxx,请检查matlab编译的dll32位还是64位,和应用程序编译配置的位数是否一样。

 

java调用matlab函数接口的两种方案_第19张图片

 

2.2.3. C++中如何调用dll动态库

由于要用C++封装matlab编译的dll,那么在C++中如何调用dll呢?

有三种方式:

配置环境的时候,在附加依赖项中已添加要用到的库:那么包含头文件后就可以直接像调用同一个项目的类或者函数一样调用了。

 

配置环境的时候,在附加依赖项中没有添加要用到的库,那么在要用库文件的cpp文件中包含头文件,并使用comment说明要加载哪个库。例如#pragma comment(lib,xxx.lib)

 

配置环境的时候,在附加依赖项中没有添加要用到的库,那么在要用库文件的cpp文件中使用windows函数加载动态库。

具体使用哪种方式看项目实际情况或者个人喜好。

 

2.2.4. C++调用matlab编译的dll中的接口时如何捕捉异常

try

{

//调用接口

}

catch(const mwException &e)

{

std::cerr<

}

catch(...)

{

}

2.2.5. C++中如何转换数据类型与Matlab生成的动态库数据接口匹配

使用C接口时数据类型的转换方法:

double -> mxArray

 

double turbine_id = 1;

mxArray *a

double id_1[]={turbine_id};

a=mxCreateDoubleMatrix(1,1,mxREAL);

memcpy(mxGetPr(a),id_1,sizeof(double));

 

mxDestroyArray(a);

a=0;

 

char* -> mxArray

 

const char* starttime = 2013-07-01 00:00:00;

mxArray *st;

st=mxCreateString(starttime);

 

更多数据类型转换可参考matlab帮助文档中的说明,如下:

 java调用matlab函数接口的两种方案_第20张图片

 

使用C++接口时数据类型的转换方法:

double -> mwArray

 

double turbineID = 1;

mwArray mTurbineID(1, 1, mxDOUBLE_CLASS);

mTurbineID.SetData(&turbineID,1);

 

char* -> mwArray

 

const char* starttime = 2013-07-01 00:00:00;

mwArray mStartTime(startTime);

mwArray还支持其他类型的数据,可参考matlab帮助文档:

 java调用matlab函数接口的两种方案_第21张图片

2.2.6. C++调用matlab dll的程序流程

主要是两个环境的初始化与终止,一个是matlab环境,一个是dll接口。

//要使用matlab的东西,要先初始化matlab环境

mclInitializeApplication()

 

//要使用matlab编译的dll,要先调用dll的初始化方法

//具体名字在matlab编译生成的头文件找

model_hhlInitialize()

 

//同上

model_hhlTerminate()

 

mclTerminateApplication()

 

需要注意mclInitializeApplication不可以重复调用,如果需要重复执行dll内容,可以使用全局变量判断是否已经初始化,再确定是否需要执行mclInitializeApplication函数初始化。

 

2.2.7. 如何在C++中调试dll

在测试程序的工程中,选中解决方案,在右键菜单中选择添加已有项目,把dll的那个工程加进来,最主要的是要保证dll工程生成的pdb调试文件要能被测试程序的工程找到才可以调试dll工程。

2.3. Java调用dll

Java调用dll有多种方法,主要是两类:

1Jni

2Jna或者Jnative之类的第三方库

网上有比较多的例子,包括数据类型转换,自行查找相关资料。


附件是两种方案的demo。或者可以到http://down.51cto.com/data/1871622 处下载本文以及demo。