如何使用LUA脚本动态解析外部智能设备的通信协议

  • LUA调用有数组参数的C函数
  • C调用LUA函数返回的表




有一个项目,需要通过串口或者TCP/IP读取一些设备的运行参数,例如温湿度、发电机等,通信协议一般是MODBUS或者电总协议的帧格式协议。为了使程序使用更多的设备,打算用LUA来解析通信协议。这样子在设备在不同的应用场合时,只需要更新或者增加LUA脚本就可以接入新的设备类型。

实现思路

  1. 每个唯一LUA文件对应特定设备的协议接入,在协议里完成数据帧的发送、接收、校验,并得到设备的运行参数,最后返回C应用程序。
  2. 要解决的问题:
1)数据的发送、CRC校验是在C程序那边完成的,所以需要解决如何将数组(表)传递给C。

       2)LUA的函数解析完了之后,如何将设备的参数返回到C,C如何得到LUA返回的参数。参数是多个,且数量不定,内容不定,可能是整形、可能是字符串。
  1. 经过搜索网上一些资料,做了一个简单测试程序,基本实现了上述的问题。剩下的就是完善和移植到现有C系统中了。实现例子:

1)

2)

以上只是实现的方法,最终的程序还要继续完善,如有机会再发布。

LUA调用有数组参数的C函数

LUA发送一包数据到串口中,发送前需要校验,校验在C完成,所以要将一个数组传递到C那边,代码如下:

  1. 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;   							//返回参数个数
}
  1. 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