【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
? ? ? ? 不知道大家有没有玩过plc设备。plc设备从本质上来说,就是一个单片机设备,只不过它的电源、输入、输出做了很多的加强措施。但是从功能说,plc又不是一个简单的io设置,好像只需要设置一个高低电平就可以了。所以使用plc的人,要想把plc用的好,让plc用在更多的场景上面,仅仅会接线是不够的,他还要知道怎么编写梯形图,或者知道怎么编写脚本。
? ? ? ? 这就我们一个启示。如果我们的上位机只是简单的应用,那么功能就不需要设置地很复杂。但是一旦大家希望把自己的上位机部署到更多的场景当中,那么不管怎么开发,都不一定能满足特定场景的需要。这个时候就有必要在上位机当中嵌入一个虚拟机脚本软件,这一点就非常重要了。某种意义上来说,这相当于给你的上位机赋予了第二次生命,软件本身有了更广阔的发展舞台,而不仅仅是我们提供什么,客户就只能做什么。今天呢,我们正好借助于lua语言,看看怎么在上位机当中嵌入一个虚拟机。
? ? ? ? 创建这个widget工程的目的仅仅是为了演示,其实纯console工程也是可以的。
? ? ? ? 既然vs支持nuget下载,那么除了c# wpf之外,我们也可以用nuget下载一下lua第三方包。输入lua,寻找到第一个选项即可,十分方便。目前下载的版本是5.4.6,还算是比较新的版本。
? ? ? ? 如果是lua调用c,那么需要利用lua_register注册一个函数。这个函数最好用extern "C"包起来,这样保证链接没有问题。当然,我们为了方便测试,直接把lua_State虚拟机变量放在了外面。
#include <iostream>
using namespace std;
#include <QtWidgets/QApplication>
#include <QDebug>
#include "lua.hpp"
#include "QtWidgetsApplication.h"
// static variable
static lua_State* L = NULL;
// lua call c
extern "C" int exampleFunction() {
const char* arg = lua_tostring(L, 1);
qDebug()<< "C++ function called with argument: " << arg ;
return 0;
}
void registerFunction(const QString& functionName, lua_CFunction function) {
lua_register(L, functionName.toUtf8().constData(), function);
}
bool executeScript(const QString& script) {
int result = luaL_dostring(L, script.toUtf8().constData());
return (result == LUA_OK);
}
? ? ? ? 如果是c调用lua函数,那么需要一开始利用luaL_dofile加载一下lua文件。文件中因为只有函数,所以等于没执行。注意lua文件要和cpp文件在同一层级的目录下。加载好了之后,就可以利用lua_pcall进行加载和处理了。
// c call lua
bool loadLuaScript(const QString& filePath) {
int result = luaL_dofile(L, filePath.toUtf8().constData());
return (result == LUA_OK);
}
void callLuaFunction(const QString& functionName, const QString& arg) {
lua_getglobal(L, functionName.toUtf8().constData());
lua_pushstring(L, arg.toUtf8().constData());
lua_pcall(L, 1, 0, 0);
}
? ? ? ? 其中LuaScript.lua内容如下所示,
-- LuaScript.lua
function luaFunctionFromCpp(arg)
print("Lua function called from C++ with argument: " .. arg)
---os.execute('mkdir 456')
end
? ? ? ? 前面虽然也写了lua调用c,以及c调用lua,但是怎么测试没有讲。这部分呢,其实不复杂。首先我们创建一个lua虚拟机,接着先测试lua调用c,然后测试c调用lua,最后删除lua虚拟机。整个test测试函数可以放在main函数的任何一个位置。
static void test()
{
// create a vm
L = luaL_newstate();
luaL_openlibs(L);
// lua call c
registerFunction("exampleFunction", (lua_CFunction)&exampleFunction);
executeScript("exampleFunction('Hello from Lua!')");
// c call lua
loadLuaScript("./LuaScript.lua");
callLuaFunction("luaFunctionFromCpp", "Hello from C++!");
// close vm
lua_close(L);
}
? ? ? ? 最后有一点需要注意下,如果是lua调用c还比较好测试,直接在c函数设置断点,就能判断有没有调用成功。如果是c调用lua,可以通过在lua文件创建目录,或者生成文件的方法,来判断lua函数是不是真得执行到了。这一点可以参考上述lua文件中被注释掉的内容。