如何使用LUA脚本动态解析外部智能设备的通信协议
- LUA调用有数组参数的C函数
- C调用LUA函数返回的表
有一个项目,需要通过串口或者TCP/IP读取一些设备的运行参数,例如温湿度、发电机等,通信协议一般是MODBUS或者电总协议的帧格式协议。为了使程序使用更多的设备,打算用LUA来解析通信协议。这样子在设备在不同的应用场合时,只需要更新或者增加LUA脚本就可以接入新的设备类型。
实现思路
- 每个唯一LUA文件对应特定设备的协议接入,在协议里完成数据帧的发送、接收、校验,并得到设备的运行参数,最后返回C应用程序。
- 要解决的问题:
1)数据的发送、CRC校验是在C程序那边完成的,所以需要解决如何将数组(表)传递给C。
2)LUA的函数解析完了之后,如何将设备的参数返回到C,C如何得到LUA返回的参数。参数是多个,且数量不定,内容不定,可能是整形、可能是字符串。
- 经过搜索网上一些资料,做了一个简单测试程序,基本实现了上述的问题。剩下的就是完善和移植到现有C系统中了。实现例子:
1)
2)
以上只是实现的方法,最终的程序还要继续完善,如有机会再发布。
LUA调用有数组参数的C函数
LUA发送一包数据到串口中,发送前需要校验,校验在C完成,所以要将一个数组传递到C那边,代码如下:
- C代码的校验函数如下
//--- lua中以数组传递参数
static int calc_chksum(lua_State *L)
{
int i;
int DatLen;
unsigned char *DatInf;
unsigned short crc;
if(!lua_istable(L, 1)){ //判断第一个参数是否为表,否则退出
lua_pushnil(L);
return -1;
}
DatLen = luaL_checknumber(L,2); //获取第二个参数,数据长度
DatInf =(unsigned char *)malloc(DatLen);
for(i=1; i<=DatLen; i++){
lua_rawgeti(L, 1, i);
DatInf[i-1] = lua_tointeger(L, -1);
lua_pop(L, 1); //把上一个内容的出栈
}
crc = CalcChkSum(DatInf, DatLen); //计算校验值
lua_pushinteger(L,(crc>>8)&0xff); //校验值高位压栈
lua_pushinteger(L,(crc)&0xff); //校验值低位压栈
free(DatInf);
return 2; //返回参数个数
}
- LUA代码如下
function collection_data(if_id, ipaddr, port, devaddr)
print("if_id:", if_id);
print("ipaddr:", ipaddr);
print("ip port:", port);
print("dev addr", devaddr);
cmd = {devaddr, 0x04, 0x00, 0x00, 0x00, 0x02};
crc1, crc2 = dev_lib.calc_chksum(cmd, 6); //调用C函数完成数组的计算得到CRC的高低字节
cmd[7] = crc1;
cmd[8] = crc2;
dev_lib.write_data(if_id, cmd, 8); //最后通过接口发送出去
end
C调用LUA函数返回的表
C语言调用LUA的程序,LUA程序返回表,C获取表格的内容。
例子如下:
C语言端:
int lua_collection_data(int if_id, char *ipaddr, int ipport, int devaddr){
float res;
int k;
double v;
const char *c;
lua_getglobal(L,"collection_data");
lua_pushnumber(L, if_id);
lua_pushstring(L, ipaddr);
lua_pushnumber(L, ipport);
lua_pushnumber(L, devaddr);
lua_call(L, 4, 1); //输入4个参数,返回1个参数(table)
lua_pushnil(L);
while(lua_next(L, -2)){
k = lua_tonumber(L, -2);
if(k==3) //表的索引3为字符串,其它为浮点点数
c = lua_tostring(L, -1);
else
v = (double)lua_tonumber(L, -1);
if(k==3)
printf("k=%d,c=%s\n", k, c); //打印测试
else
printf("k=%d,v=%f\n", k, v);
lua_pop(L,1);
}
return res;
}
LUA语言端:
function collection_data(if_id, ipaddr, port, devaddr)
print("if_id:", if_id);
print("ipaddr:", ipaddr);
print("ip port:", port);
print("dev addr", devaddr);
cmd = {devaddr, 0x04, 0x00, 0x00, 0x00, 0x02};
crc1, crc2 = dev_lib.calc_chksum(cmd, 6);
cmd[7] = crc1;
cmd[8] = crc2;
dev_lib.write_data(if_id, cmd, 8);
--以下是测试用的
spots[1] = 12.45
spots[2] = 32
spots[3] = "TEST MESSAGE"
--return 10,12,13;
return spots --返回表格
end
测试结果:
k=1,v=12.450000
k=2,v=32.000000
k=3,c=TEST MESSAGE