远程计算_使用Matlab计算引擎

#0 环境

  • win7 32bit
  • vs2008
  • matlab r2013b

#1 准备工作

  1. 确保vs与matlab均已正确安装

  2. 添加环境变量PATHD:\Program Files\MATLAB\R2013b\bin\win32,(我的matlab安装在D:\Program Files\MATLAB\,如果不是这个路径则做相应修改,64bit系统路径最后则为win64,下同)

  3. vs设置,工具 -> 选项 -> 项目和解决方案 -> VC++ 路径,Platform选择Win32,然后
    Include files中添加 D:\Program Files\MATLAB\R2013b\extern\include
    Library files中添加 D:\Program Files\MATLAB\R2013b\extern\lib\win32\microsoft
    注意高版本的VS如2013已经将该设置迁移到项目属性中。

  4. 附加依赖项设置,项目属性 -> 配置属性 -> 链接器 -> 输入 -> 附加依赖项,编辑加入libeng.lib。(也可直接在代码中加入#pragma comment(lib, "libeng.lib")语句)

    关于以上路径设置的说明
    环境变量Path:指定系统搜索路径(此处用于查找libeng.dll等dll文件),也可不设置而是将该路径下所有用到的dll文件拷贝到当前路径中
    头文件路径:指定头文件的搜索路径(此处为engine.h文件以及该文件中包含的其他头文件),也可不设置,但包含头文件时需要使用完整路径,这样头文件较多时会比较麻烦
    库文件路径:指定库文件的搜索路径(此处为libeng.lib文件),可以不设置,但#pragma comment时需要使用lib文件的完整路径,或者将该文件拷贝到当前路径下

#2 引擎函数介绍
下表列出了本文用到的所有引擎函数

函数说明
engOpen启动matlab计算引擎(创建了一个matlab进程并建立联系)
engClose关闭引擎(关闭创建的matlab进程)
engSetVisible设置引擎窗口是否可见
engOutputBuffer设置引擎文本输出缓冲区(每次使用engEvalString执行新命令都会使缓冲区清空并填入新的结果),执行代码engOutputBuffer(ep, NULL, 0)可停用缓冲区
engEvalString执行matlab命令
  1. engOpen
    • [函数原型]Engine* engOpen(const char* startcmd)
    • [参数说明] 用于启动matlab的字符串命令,Windows中为NULL
    • [返回值] 引擎指针
  2. engClose
    • [函数原型]int engClose(Engine* ep)
    • [参数说明] 引擎指针,即engClose函数的返回值
    • [返回值] 0成功,1失败
  3. engSetVisible
    • [函数原型]int engSetVisible(Engine* ep, bool newVal)
    • [参数说明]ep为引擎指针;newValtrue表示显示引擎窗口,false表示隐藏引擎窗口
    • [返回值] 0成功,1失败
  4. engOutputBuffer
    • [函数原型]int engOutputBuffer(Engine* ep, char* buffer, int buflen)
    • [参数说明]ep为引擎指针;buffer为文本输出缓冲区指针;buflen为缓冲区大小
    • [返回值] 0成功,1失败
  5. engEvalString
    • [函数原型]int engEvalString(Engine* ep, const char* string)
    • [参数说明]ep为引擎指针;string为要执行的命令文本
    • [返回值] 0成功,1失败

#3 引擎使用
包括打开与设置引擎、执行命令、关闭引擎三个部分

  1. 打开引擎并设置。包括打开引擎、设置不可见、设置输出缓冲区以及设置工作空间为当前路径,代码如下

    // 打开并设置Matlab引擎
    int MatlabOpen(Engine** ep, const char* sPath, char* sBuffer, int nBufLen)
    {
    	// 启动matlab引擎
    	if (!(*ep = engOpen(NULL)))	// 启动引擎
    	{
    		return 1;	// 启动失败
    	}
    	// 设置
    	engSetVisible(*ep, false);		// 设置窗口不可视
    	engOutputBuffer(*ep, sBuffer, nBufLen);	// 设置Matlab输出缓冲区
    	// 修改工作路径
    	char sWorkPath[MAX_PATH];
    	sprintf_s(sWorkPath, MAX_PATH, "cd '%s'", sPath);
    	engEvalString(*ep, sWorkPath);
    	return 0;
    }
    
  2. 关闭引擎。代码如下

    // 关闭Matlab引擎
    int MatlabClose(Engine* ep)
    {
    	return engClose(ep);
    }
    
  3. 执行命令。执行命令获取文本结果,如果有画图命令则将Figure保存为图片

    // 执行Matlab命令
    // 返回图片个数
    int MatlabExec(Engine* ep, const char* mCmd)
    {
    	int nFig = 0;	// 图片个数
    	engOutputBuffer(ep, matbuf, MATBUFSIZE);	// 恢复输出缓冲区
    	engEvalString(ep, mCmd);	// 执行命令
    	engOutputBuffer(ep, matbuffig, MATBUFFIGSIZE);	// 临时缓冲区,保存检测是否有Figure的结果
    	// 判断是否有figure并处理
    	engEvalString(ep, "isempty(findall(0,'Type','Figure'))");
    	if (strlen(matbuffig) > 13 && '0' == matbuffig[13])	// 有Figure
    	{
    		char savefig[100] = {0};
    		char gettype[20] = {0};
    		int i = 1;
    		sprintf_s(gettype, 20, "get(1,'Type');");
    		engEvalString(ep, gettype);
    		while(matbuffig[0] == 0)	// 空
    		{
    			// 将Figure保存为图片
    			sprintf_s(savefig, 100, "set(%d,'color','white');f=getframe(%d);imwrite(f.cdata,'figure%d.png');", i, i, i);
    			engEvalString(ep, savefig);
    			nFig++;
    			// 下一个
    			sprintf_s(gettype, 20, "get(%d,'Type');", ++i);
    			engEvalString(ep, gettype);
    		}
    		engEvalString(ep, "close all");	// 关闭所有Figure
    	}
    	return nFig;	// 返回图片个数
    }
    

#4 示例
由于关于Matlab计算引擎的操作都要在同一个线程内执行,而项目需要在其他多个线程中执行matlab命令,因此考虑采用线程消息的方式实现。即在主线程中启用计算引擎,其他线程通过向主线程发送消息来请求计算,命令通过消息参数传递。完整代码如下

#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include "engine.h"

#pragma comment(lib, "libeng.lib")

#define WM_CALC WM_USER+200   // 请求计算消息

// 全局变量
#define MATBUFSIZE 10240
#define MATBUFFIGSIZE 256
char matbuf[MATBUFSIZE] = {0};			// Matlab输出缓冲区
char matbuffig[MATBUFFIGSIZE] = {0};	// 临时输出缓冲区
DWORD dwMainThreadId = 0;				// 主线程ID

// 函数声明
DWORD WINAPI ThreadTest(LPVOID lpParam);	// 测试线程
int MatlabOpen(Engine** ep, const char* sPath, char* sBuffer, int nBufLen);
int MatlabClose(Engine* ep);
int MatlabExec(Engine* ep, const char* mCmd);


int APIENTRY WinMain(HINSTANCE hInstance, 
					 HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	// 获取线程ID
	dwMainThreadId = GetCurrentThreadId();

	// 启动matlab引擎
	Engine* ep = NULL;			// matlab引擎
	char path[MAX_PATH];		// 当前路径
	strcpy_s(path, MAX_PATH, __argv[0]);
	(_tcsrchr(path, '\\'))[1] = 0;	// 只保留路径部分
	if (MatlabOpen(&ep, path, matbuf, MATBUFSIZE))
	{
		return 0;
	}

	// 创建测试线程
	CreateThread(NULL, 0, ThreadTest, 0, 0, NULL);

	// 线程消息循环
	MSG msg;
	PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
	while(GetMessage(&msg, NULL, 0, 0))
	{
		if (msg.message == WM_CALC)	// 请求计算消息
		{
			MatlabExec(ep, (char*)msg.wParam);	// 执行matlab命令
		}
	}

	// 关闭matlab引擎
	MatlabClose(ep);

	return (int) msg.wParam;
}

// 测试线程
DWORD WINAPI ThreadTest(LPVOID lpParam)
{
	Sleep(1000);
	PostThreadMessage(dwMainThreadId, WM_CALC, WPARAM("pi"), 0);	// 输出pi
	Sleep(1000);
	PostThreadMessage(dwMainThreadId, WM_CALC, WPARAM("plot(sin(0:0.1:10))"), 0);	// 画正弦曲线
	Sleep(5000);
	PostThreadMessage(dwMainThreadId, WM_QUIT, 0, 0);	// 退出程序
	return 0;
}

// MatlabOpen、MatlabClose、MatlabExec三个函数的实现分别见3.1、3.2、3.3,此处不再列出
// ……

测试结果:

  • 第一次计算后matbuf值为"ans = 3.1416"
  • exe路径下生成文件figure1.png,如下图
    figure1.png

本文原文链接 http://blog.csdn.net/yanglx2022/article/details/46477025


版权声明:本文为yanglx2022原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。