基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(九)使用domoticz+mosquitto+Android客户端实现控制mini2440上的LED(二)

为了充分利用domoticz平台的对MQTT客户端的控制功能,现在,受控设备端代码的核心任务转移到了对domoticz/out主题的MQTT消息解析上。本文将设计一个简单框架来实现对其消息的解析和功能回调。


一、对消息参数名字稍作研究。



domoticz/out的MQTT消息格式参考:
https://www.domoticz.com/wiki/MQTT
根据官方资料,并没有指明参数个数。
实际上看domoticz-3.5877版本源代码,可以在domoticz-3.5877/hardware/MQTT.cpp中看出,
domoticz/out消息大概有两类:
1、普通设备信息。
在void MQTT::SendDeviceInfo(const int m_HwdID, const unsigned long long DeviceRowIdx, const std::string &DeviceName, const unsigned char *pRXCommand)
中进行封装。

我们的开关设备也就在这里封装的。



从这段代码中可以看到:

10个参数名字是固定的

["idx"]

["id"] 

["unit"]

["name"] 

["dtype"] 

["stype"] 

["switchType"]

["RSSI"]

["Battery"]

["nvalue"]

还有个参数名字不固定:

"svalue" 

它可以是以后缀1为开头:"svalue1" 或者"svalue2"、……"svalue(n)" 

这意味着参数数量总体是不固定的。


我们实际的开关量收到的消息如下:

{
   "Battery" : 255,
   "RSSI" : 12,
   "dtype" : "Light/Switch",
   "id" : "00014051",
   "idx" : 1,
   "name" : "LED鐏,
   "nvalue" : 1,
   "stype" : "Switch",
   "svalue1" : "0",
   "switchType" : "On/Off",
   "unit" : 1
}

收到了"svalue1"

要写出一个灵活的框架来适应svalue数量可变,还是有点难度的,先不做这么复杂的。

后文中将写一个能够接收名字固定的参数,再加上"svalue1" "svalue2" ,共处理12个参数类型。(或许有时间写个对"svalue"参数数量可变的处理函数?)

当然,domoticz源码是公开的,我们也可以在c++级别源代码上添加自己所需要的参数。


2、场景消息。

void MQTT::SendSceneInfo(const unsigned long long SceneIdx, const std::string &SceneName)

中进行封装的。

暂时没用到,暂不分析。

详情请参考相关资料和源代码。


二、编写一个简单的消息解析框架。

框架设计如下:



图中红色箭头的过程就是我们主要要实现的过程。

右边两个虚边框就是我们设计的框架。

具体实现代码如下:

CommonTypes.h:

/******************************************************************************
*filename: CommonTypes.h
******************************************************************************/


#ifndef COMMON_TYPES_H
#define COMMON_TYPES_H

#ifdef __cplusplus
extern "C"
{
#endif

//------------------------------------------------------------------------------
//common defines

#define KEY_IDX		0
#define KEY_NAME	1
#define KEY_ID		2
#define KEY_UINT	3
#define KEY_DTYPE	4
#define KEY_STYPE	5
#define KEY_NVALUE	6
#define KEY_SVALUE1	7
#define KEY_SVALUE2	8
#define KEY_BATTERY	9
#define KEY_RSSI	10
#define KEY_SWITCH_TYPE	11

#define MSG_MAX_LEN 128
#define KEY_WORDS_NUM 12

//------------------------------------------------------------------------------
//common types
typedef enum 
{
	STRING=0,
	INT=1,
	UINT=2
}ARG_TYPE_t;

#ifdef __cplusplus
}
#endif

#endif /* #ifndef COMMON_TYPES_H */
/*-- File end --*/


HardwareInterface.h:


/******************************************************************************
*filename: HardwareInterface.h
******************************************************************************/
#ifndef HARDWARE_INTERFACE_H
#define HARDWARE_INTERFACE_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "CommonTypes.h"

//------------------------------------------------------------------------------
//common Hardware interface
typedef struct
{ 
	//解析出来的消息参数类型(STRING、INT、UINT中的一种)
	ARG_TYPE_t type;
	
	//下面是解析出来的具体消息参数数据
	union
	{
		char strArg[MSG_MAX_LEN];
		int iVar;
		unsigned int uiVar;
	};
}ParserArg;


typedef struct
{
	//----------------respons parser interface----------------------------------
	//int(*IDX_ParserCallback)(ParserArg* arg);//无需处理idx
	int(*NAME_ParserCallback)(ParserArg* arg);
	int(*ID_ParserCallback)(ParserArg* arg);
	int(*UINT_ParserCallback)(ParserArg* arg);
	int(*DTYPE_ParserCallback)(ParserArg* arg);
	int(*STYPE_ParserCallback)(ParserArg* arg);
	int(*NVALUE_ParserCallback)(ParserArg* arg);
	int(*SVALUE1_ParserCallback)(ParserArg* arg);
	int(*SVALUE2_ParserCallback)(ParserArg* arg);
	int(*BATTERY_ParserCallback)(ParserArg* arg);
	int(*RSSI_ParserCallback)(ParserArg* arg);
	int(*SWITCH_TYPE_ParserCallback)(ParserArg* arg);
	ParserArg parseArg;
	int RegisterIDX;
	
	//--------------device base operation---------------------------------------
	//must be implement
	int (*Open)();
	void (*Init)();
	void (*Close)();
	
}Hardware;

typedef int(*ParserCallback)(ParserArg* arg);

#ifdef __cplusplus
}
#endif

#endif /* #ifndef HARDWARE_INTERFACE_H */
/*-- File end --*/


HardwareControl.h:


/******************************************************************************
* filename: HardwareControl.h
******************************************************************************/


#ifndef HARDWARE_CONTROL_H
#define HARDWARE_CONTROL_H
#ifdef __cplusplus
extern "C"
{
#endif

#include "HardwareInterface.h"

#define HARDWARE_MAX_NUM 32

#define REGISTER_SUCCESED 1
#define REGISTER_ERR1 -1 //索引号已经被使用
#define REGISTER_ERR2 -2 //容器已满,不能注册

extern int OpenHardwares();
extern void initHardwareSettings();
extern void CloseHardwares();

extern int RegisterHaraware(Hardware *hardware,int idx);
extern Hardware* GetHardware(int idx);
extern int UnregisterHaraware(int idx);

#ifdef __cplusplus
}
#endif

#endif /* #ifndef HARDWARE_CONTROL_H */
/*-- File end --*/




HardwareControl.c:


/******************************************************************************
* filename: HardwareControl.c
******************************************************************************/
#include "HardwareControl.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>


//------------------------------------------------------------------------------
//GetHardWare interface
Hardware* g_HardwareContainer[HARDWARE_MAX_NUM];


/******************************************************************************
* 函数名: RegisterHaraware
* 功能描述:向硬件容器注册一个索引号为idx的硬件
* 参数1 :Hardware *hardware [I]:该硬件的指针
* 参数2 :int idx [I]:要分配的索引号
* 返回值: int ,成功则返回1,失败则返回错误号
* 创建时间:2017-Apr-17 22:08:58
* 修改时间:2017-Apr-17 22:08:58
* 版本记录:
* 其他说明:为了使用方便应该做一个配置文件以适配硬件信息
******************************************************************************/

int RegisterHaraware(Hardware *hardware,int idx)
{
	int i;
	assert(hardware);
	for(i=0;i<HARDWARE_MAX_NUM;i++)
	{
		if(g_HardwareContainer[i])
		{
			if(g_HardwareContainer[i]->RegisterIDX==idx)
				return REGISTER_ERR1;
			else
				continue;
		}
		else
		{
			g_HardwareContainer[i] = hardware ;
			g_HardwareContainer[i]->RegisterIDX = idx;
			return 1;	
		}
	}
	return REGISTER_ERR2;	
}

/******************************************************************************
* 函数名: GetHardWare
* 功能描述: 根据索引号获取相应的硬件设备指针
* 参数1 :int idx [I]:设备索引号
* 返回值: 成功则返回对应硬件指针,失败返回0(NULL)
* 创建时间:2017-Apr-16 18:52:10
* 修改时间:2017-Apr-16 18:52:10
* 版本记录:
******************************************************************************/
Hardware* GetHardware(int idx)
{
	int i;
	for(i=0;i<HARDWARE_MAX_NUM;i++)
	{
		if(g_HardwareContainer[i] && g_HardwareContainer[i]->RegisterIDX==idx)
			return g_HardwareContainer[i];
	}
	return 0;
}

/******************************************************************************
* 函数名: UnregisterHaraware
* 功能描述:取消索引号为idx的硬件注册
* 参数1 :int idx [I]:要取消注册的硬件的idx号
* 返回值: 成功则返回取消注册的位置,失败返回-1
* 创建时间:2017-Apr-17 22:06:25
* 修改时间:2017-Apr-17 22:06:25
* 版本记录:
******************************************************************************/
int UnregisterHaraware(int idx)
{
	int i;
	for(i=0;i<HARDWARE_MAX_NUM;i++)
	{
		if(g_HardwareContainer[i] && g_HardwareContainer[i]->RegisterIDX==idx)
			g_HardwareContainer[i] = 0;
		return i;
	}
	return -1;
}

//------------------------------------------------------------------------------
//initionalize

int OpenHardwares()
{
	int i;
	int count=0;
	for(i=0;i<HARDWARE_MAX_NUM;i++)
	{
		if(g_HardwareContainer[i])
		{
			if(!g_HardwareContainer[i]->Open)
				return -i;//如果该硬件接口没有实现Open,则返回它在容器中的位置的相反数(<=0)
			else
			{
				g_HardwareContainer[i]->Open();
				count++;
			}
		}
	}
	
	return count;//如果成功返回执行Open的设备数量
}

void initHardwareSettings()
{
	int i;
	for(i=0;i<HARDWARE_MAX_NUM;i++)
	{
		if(g_HardwareContainer[i] && g_HardwareContainer[i]->Init)
		{
			g_HardwareContainer[i]->Init();
		}
	}
}

void CloseHardwares()
{
	int i;
	for(i=0;i<HARDWARE_MAX_NUM;i++)
	{
		if(g_HardwareContainer[i] && g_HardwareContainer[i]->Close)
			g_HardwareContainer[i]->Close();
	}
}

/*-- File end --*/




DomoticzMessageParser.h:


/******************************************************************************
* filename: DomoticzMessageParser.h
******************************************************************************/


#ifndef DOMOTICZ_MESSAGE_PARSER_H
#define DOMOTICZ_MESSAGE_PARSER_H
#ifdef __cplusplus
extern "C"
{
#endif

#include "HardwareInterface.h"

typedef struct{
	char *str;//分割字符串后,消息存入buf中时所需对比的关键字
	char *parseStr;//解析消息时所使用的匹配字符串
	ARG_TYPE_t type;//该消息对应的类型(STRING、INT、UINT中的一种)
}KeyWord_t;


extern int GetKeywordIndex(const char* str);



//------------------------------------------------------------------------------
//common DomoitczMessageParser interface

typedef struct DomoitczMessageParser DomoitczMessageParser;

struct DomoitczMessageParser
{
	int(*IDX_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*NAME_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*ID_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*UINT_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*DTYPE_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*STYPE_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*NVALUE_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*SVALUE1_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*SVALUE2_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*BATTERY_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*RSSI_Handler)(DomoitczMessageParser* pParser, const char* message);
	int(*SWITCH_TYPE_Handler)(DomoitczMessageParser* pParser, const char* message);
	int (*FillArgStr)(DomoitczMessageParser* pParser,const char* value);
	char MsgBuf[KEY_WORDS_NUM][MSG_MAX_LEN];	
	Hardware* bindHardware;
};

typedef	int(*DomoitczMessageParserHandler)(DomoitczMessageParser* pParser, const char* message);

extern DomoitczMessageParser g_DMP;
extern DomoitczMessageParser* g_pParser;

extern void SetupDomoitczMessageParser();

extern void SetEnableParseItem(int item);
extern void SetDisableParseItem(int item);

extern int ParseDomoticzMessage(char* str);
//------------------------------------------------------------------------------
//hardware settings
extern void initHardWareSettings();


#ifdef __cplusplus
}
#endif

#endif /* #ifndef DOMOTICZ_MESSAGE_PARSER_H */
/*-- File end --*/




DomoticzMessageParser.c:


/******************************************************************************
* filename: DomoticzMessageParser.c
******************************************************************************/

/*-- #include --*/
#include "DomoticzMessageParser.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>

#ifdef _DEBUG
#define dprintf(msg,...)  printf("%s,line:%d,"msg,__FILE__,__LINE__,##__VA_ARGS__)
#else
#define dprintf(msg,...)
#endif


KeyWord_t KeyWords[KEY_WORDS_NUM+1]=
{
	{"idx","   \"idx\" : %d,",INT},
	{"name","   \"name\" : \"%s\",",STRING},
	{"id","   \"id\" : \"%s\",",STRING},
	{"unit","   \"unit\" : %u",UINT},
	{"dtype","   \"dtype\" : \"%s\",",STRING},
	{"stype","   \"stype\" : \"%s\",",STRING},
	{"nvalue","   \"nvalue\" : %d,",INT},
	{"svalue1","   \"svalue1\" : \"%s\",",STRING},
	{"svalue2","   \"svalue2\" : \"%s\",",STRING},
	{"Battery","   \"Battery\" : %u,",UINT},
	{"RSSI","   \"RSSI\" : %d,",INT},
	{"switchType","   \"switchType\" : \"%s\",",STRING},
		
	{"unknown","unknown",STRING}//防止越界访问	
};

/******************************************************************************
* 函数名: GetKeywordIndex
* 功能描述: 根据关键字获取含该关键字的消息在KeyWords的位置索引号
* 参数1 :const char* str [I]:要查询的具体关键字字符串
* 返回值: 消息在KeyWords的位置索引号
* 创建时间:2017-Apr-16 19:09:26
* 修改时间:2017-Apr-16 19:09:26
* 版本记录:
******************************************************************************/
int GetKeywordIndex(const char* str)
{
	int i;
	for(i=0;i<KEY_WORDS_NUM;i++)
	{
		if(strstr(str,KeyWords[i].str))
		{
			return i;
		}
	}
	return KEY_WORDS_NUM;
}


//------------------------------------------------------------------------------
//DomoitczMessageParser interface implemention
//#0
int IDX_HandlerImpl(DomoitczMessageParser* pParser, const char* message)
{
	int idx;
	if(!pParser)
		return 0;		
	if(sscanf(message,KeyWords[KEY_IDX].parseStr,&idx)>0)
	{
		dprintf("idx=%d\n",idx);		
		pParser->bindHardware = GetHardware(idx);//根据设备索引号搜索硬件设备
		//pParser->bindHardware->IDX_ParserCallback(&(pParser->bindHardware->parseArg));
		return pParser->bindHardware?1:0;		
	}
	return 0;
}

//#1
int NAME_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->NAME_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->NAME_ParserCallback;
	if(sscanf(message,KeyWords[KEY_NAME].parseStr,pParser->bindHardware->parseArg.strArg)>0)
	{
		dprintf("name=%s\n",pParser->bindHardware->parseArg.strArg);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_NAME].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#2
int ID_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->ID_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->ID_ParserCallback;	
	if(sscanf(message,KeyWords[KEY_ID].parseStr,pParser->bindHardware->parseArg.strArg)>0)
	{
		dprintf("id=%s\n",pParser->bindHardware->parseArg.strArg);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_ID].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#3
int UINT_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->UINT_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->UINT_ParserCallback;	
	if(sscanf(message,KeyWords[KEY_UINT].parseStr,&(pParser->bindHardware->parseArg.uiVar))>0)
	{
		dprintf("uint=%u\n",pParser->bindHardware->parseArg.uiVar);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_UINT].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#4
int DTYPE_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->DTYPE_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->DTYPE_ParserCallback;		
	if(sscanf(message,KeyWords[KEY_DTYPE].parseStr,pParser->bindHardware->parseArg.strArg)>0)
	{
		dprintf("dtype=%s\n",pParser->bindHardware->parseArg.strArg);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_DTYPE].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#5
int STYPE_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->STYPE_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->STYPE_ParserCallback;	
	if(sscanf(message,KeyWords[KEY_STYPE].parseStr,pParser->bindHardware->parseArg.strArg)>0)
	{
		dprintf("name=%s\n",pParser->bindHardware->parseArg.strArg);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_STYPE].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#6
int NVALUE_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser || !pParser->bindHardware || !pParser->bindHardware->NVALUE_ParserCallback)
		return 0;
	funcParseCallback = pParser->bindHardware->NVALUE_ParserCallback;		
	if(sscanf(message,KeyWords[KEY_NVALUE].parseStr,&(pParser->bindHardware->parseArg.iVar))>0)
	{
		dprintf("nvalue=%d\n",pParser->bindHardware->parseArg.iVar);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_NVALUE].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#7
int SVALUE1_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->SVALUE1_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->SVALUE1_ParserCallback;		
	if(sscanf(message,KeyWords[KEY_SVALUE1].parseStr,pParser->bindHardware->parseArg.strArg)>0)
	{
		dprintf("svalue1=%s\n",pParser->bindHardware->parseArg.strArg);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_SVALUE1].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#8
int SVALUE2_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->SVALUE2_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->SVALUE2_ParserCallback;
	if(sscanf(message,KeyWords[KEY_SVALUE2].parseStr,pParser->bindHardware->parseArg.strArg)>0)
	{
		dprintf("svalue2=%s\n",pParser->bindHardware->parseArg.strArg);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_SVALUE2].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#9
int BATTERY_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->BATTERY_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->BATTERY_ParserCallback;
	if(sscanf(message,KeyWords[KEY_BATTERY].parseStr,&(pParser->bindHardware->parseArg.uiVar))>0)
	{
		dprintf("battery=%u\n",pParser->bindHardware->parseArg.uiVar);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_BATTERY].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;
	}
	return 0;
}

//#10
int RSSI_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->RSSI_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->RSSI_ParserCallback;
	if(sscanf(message,KeyWords[KEY_RSSI].parseStr,&(pParser->bindHardware->parseArg.iVar))>0)
	{
		dprintf("RSSI=%d\n",pParser->bindHardware->parseArg.iVar);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_RSSI].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;		
	}
	return 0;
}

//#11
int SWITCH_TYPE_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{
	ParserCallback funcParseCallback;
	if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->SWITCH_TYPE_ParserCallback)
		return 0;	
	funcParseCallback = pParser->bindHardware->SWITCH_TYPE_ParserCallback;
	if(sscanf(message,KeyWords[KEY_SWITCH_TYPE].parseStr,pParser->bindHardware->parseArg.strArg)>0)
	{
		dprintf("switchType=%s\n",pParser->bindHardware->parseArg.strArg);
		pParser->bindHardware->parseArg.type = KeyWords[KEY_SWITCH_TYPE].type;
		funcParseCallback(&(pParser->bindHardware->parseArg));
		return 1;		
	}
	return 0;
}

/******************************************************************************
* 函数名: FillArgStrImpl
* 功能描述: .
* 参数1 :DomoitczMessageParser* pParser [I]:param description.
* 参数2 :const char* value [I]:param description.
* 返回值: int return variable description.
* 创建时间:2017-Apr-16 19:16:58
* 修改时间:2017-Apr-16 19:16:58
* 版本记录:
******************************************************************************/
int FillArgStrImpl(DomoitczMessageParser* pParser,const char* value)
{
	int key;
	if(!pParser)
		return -1;
	key = GetKeywordIndex(value);
	if(key>=KEY_WORDS_NUM)
		return -1;
	strcpy(pParser->MsgBuf[key],value);
	return key;
}

//------------------------------------------------------------------------------
//Setup DomoitczMessageParser

static int CALL_PARSER_FUNC_FLAG = 0;

DomoitczMessageParser g_DMP;
DomoitczMessageParser* g_pParser = &g_DMP;

static DomoitczMessageParserHandler HandlerPool[KEY_WORDS_NUM];

/******************************************************************************
* 函数名: SetupDomoitczMessageParser
* 功能描述: 构建消息解析器
* 参数1 :DomoitczMessageParser* pDMP [I]:要构建的domoticz消息解析器指针
* 参数2 :Hardware* bindHardware [I]:初始化消息解析器解析回调对象
* 返回值: 
* 创建时间:2017-Apr-16 19:13:29
* 修改时间:2017-Apr-16 19:13:29
* 版本记录:
******************************************************************************/
void SetupDomoitczMessageParser()
{
	g_pParser->IDX_Handler = IDX_HandlerImpl;
	g_pParser->NAME_Handler = NAME_HandlerImpl;
	g_pParser->ID_Handler = ID_HandlerImpl;
	g_pParser->UINT_Handler = UINT_HandlerImpl;
	g_pParser->DTYPE_Handler = DTYPE_HandlerImpl;
	g_pParser->STYPE_Handler = STYPE_HandlerImpl;
	g_pParser->NVALUE_Handler = NVALUE_HandlerImpl;
	g_pParser->SVALUE1_Handler = SVALUE1_HandlerImpl;
	g_pParser->SVALUE2_Handler = SVALUE2_HandlerImpl;
	g_pParser->BATTERY_Handler = BATTERY_HandlerImpl;
	g_pParser->RSSI_Handler = RSSI_HandlerImpl;
	g_pParser->SWITCH_TYPE_Handler = SWITCH_TYPE_HandlerImpl;
	g_pParser->bindHardware = 0;
	g_pParser->FillArgStr = FillArgStrImpl;

	HandlerPool[KEY_IDX] = IDX_HandlerImpl;
	HandlerPool[KEY_NAME] = NAME_HandlerImpl;	
	HandlerPool[KEY_ID] = 	ID_HandlerImpl;	
	HandlerPool[KEY_UINT] = UINT_HandlerImpl;	
	HandlerPool[KEY_DTYPE] = DTYPE_HandlerImpl;
	HandlerPool[KEY_STYPE] = STYPE_HandlerImpl;
	HandlerPool[KEY_NVALUE] = NVALUE_HandlerImpl;	
	HandlerPool[KEY_SVALUE1] = 	SVALUE1_HandlerImpl;
	HandlerPool[KEY_SVALUE2] = SVALUE2_HandlerImpl	;
	HandlerPool[KEY_BATTERY] = 	BATTERY_HandlerImpl;
	HandlerPool[KEY_RSSI] = RSSI_HandlerImpl;
	HandlerPool[KEY_SWITCH_TYPE] = SWITCH_TYPE_HandlerImpl;

}


// 将str字符以spl分割,存于g_pParser->MsgBuf中,并返回子字符串数量
int split(char* str, const char* delim)
{
    int n = 0;
    char *result = NULL;
	assert(g_pParser);
    result = strtok(str, delim);
    while( result != NULL )
    {		
		g_pParser->FillArgStr(g_pParser,result);
		dprintf("result=%s\n",result);
        result = strtok(NULL, delim);
    }
    return n;
}



/******************************************************************************
* 函数名: SetEnableParseItem
* 功能描述: 设置CALLPARSER_FUNC_FLAG的item位置上的标志位为1
* 参数1 :int item [I]:要置1的位置,由右往左数0,1,2,3,...。
* 该参数不能超过KEY_WORDS_NUM,且不能超过31,否则视为无效。
*
* 返回值: 
* 创建时间:2017-Apr-16 20:15:51
* 修改时间:2017-Apr-16 20:15:51
* 版本记录:
******************************************************************************/
void SetEnableParseItem(int item)
{
	assert(item<32);
	if(item>=0 && item<KEY_WORDS_NUM)
	{
		CALL_PARSER_FUNC_FLAG |= 1<<item;
	}
}

/******************************************************************************
* 函数名: SetEnableParseItem
* 功能描述: 设置CALLPARSER_FUNC_FLAG的item位置上的标志位为0
* 参数1 :int item [I]:要清零的位置,由右往左数0,1,2,3,...。
* 该参数不能超过KEY_WORDS_NUM,且不能超过31,否则视为无效。
*
* 返回值: 
* 创建时间:2017-Apr-16 20:15:51
* 修改时间:2017-Apr-16 20:15:51
* 版本记录:
******************************************************************************/
void SetDisableParseItem(int item)
{
	assert(item<32);
	if(item>=0 && item<KEY_WORDS_NUM)
	{
		CALL_PARSER_FUNC_FLAG &= ~(1<<item);
	}
}



/******************************************************************************
* 函数名: ParseDomoticzMessage
* 功能描述: 解析消息,并回调与消息相应的硬件处理函数
* 参数1 :char* str [I]:要解析的目标消息字符串
* 返回值: int 
* 创建时间:2017-Apr-16 19:18:17
* 修改时间:2017-Apr-16 19:18:17
* 版本记录:
******************************************************************************/
int ParseDomoticzMessage(char* str)
{	
	int nCount ;
	//printf("---------------------------------------\n");
	int i;
	int CallFlag ;
	nCount = split(str,"\n");
	//SetDisableParseItem(KEY_SWITCH_TYPE);		
	CallFlag = CALL_PARSER_FUNC_FLAG;
	//dprintf("CALL_PARSER_FUNC_FLAG=0x%X\n",CALL_PARSER_FUNC_FLAG);
	for(i=0;i<KEY_WORDS_NUM && i<32;i++)
	{
		if(CallFlag&0x1)
		{
			HandlerPool[i](g_pParser,g_pParser->MsgBuf[i]);
			//dprintf("i=%d\n",i);
		}
		CallFlag>>=1;
	}

	//g_pParser->IDX_Handler(g_pParser,g_pParser->MsgBuf[KEY_IDX]);
	//g_pParser->NVALUE_Handler(g_pParser,g_pParser->MsgBuf[KEY_NVALUE]);

	return 1;
}


/*-- File end --*/



上面是框架,下面来两个例子(LED0、LED1):


LED0.h:


/******************************************************************************
* filename: LED0.h
******************************************************************************/
#ifndef LED0_H
#define LED0_H

#include "HardwareInterface.h"

extern Hardware* Create_LED0();

#endif /* #ifndef LED0_H */
/*-- File end --*/


LED0.c:


#include "LED0.h"

extern int led_fd;

static Hardware LED0;
static int on;
static int led_no = 0;

/*
因为LED0~LED3均使用的led_fd文件描述符,应该统一执行打开、关闭,所以下面实现为
空操作
*/
int LED0_Open()
{

}
	
void LED0_Init()
{
	ioctl(led_fd, 0, led_no);
}

void LED0_Close()
{

}


/******************************************************************************
* 函数名: LED0_NVALUE_ParserCallbackImpl
* 功能描述: 在DomoiticzMessageParser进行解析"nvalue"消息参数后,
* 被回调以执行相应功能
*
* 参数1 :ParserArg* arg [I]:已经解析的消息参数
* 返回值: 成功返回1,失败返回0
* 创建时间:2017-Apr-16 18:50:27
* 修改时间:2017-Apr-16 18:50:27
* 版本记录:
******************************************************************************/
int LED0_NVALUE_ParserCallbackImpl(ParserArg* arg)
{
	//printf("LED0_IDX_ParserCallbackImpl is called!\n");
	if(arg && arg->type==INT)
	{
		on = arg->iVar;
		ioctl(led_fd, on, led_no);
		return 1;
	}
	
	return 0;
}

int LED0_SWITCH_TYPE_ParserCallbackImpl(ParserArg* arg)
{
	//printf("LED0_SWITCH_TYPE_ParserCallbackImpl is called!\n");	
	if(arg && arg->type==STRING)
	{
		printf("%s\n",arg->strArg);	
		return 1;
	}
	
	return 0;
}

Hardware* Create_LED0()
{
	LED0.Open = LED0_Open;
	LED0.Init= LED0_Init;
	LED0.Close= LED0_Close;
	LED0.NVALUE_ParserCallback = LED0_NVALUE_ParserCallbackImpl;
	LED0.SWITCH_TYPE_ParserCallback = LED0_SWITCH_TYPE_ParserCallbackImpl;
	return &LED0;
}






LED1.h:

/******************************************************************************
* filename: LED1.h
******************************************************************************/
#ifndef LED1_H
#define LED1_H

#include "HardwareInterface.h"

extern Hardware* Create_LED1();

#endif /* #ifndef LED0_H */
/*-- File end --*/




LED1.c:


#include "LED1.h"

extern int led_fd;

static Hardware LED1;
static int on;
static int led_no = 1;

int LED1_Open()
{

}
	
void LED1_Init()
{
	ioctl(led_fd, 0, led_no);
}

void LED1_Close()
{

}


int LED1_NVALUE_ParserCallbackImpl(ParserArg* arg)
{
	//printf("LED0_IDX_ParserCallbackImpl is called!\n");
	if(arg && arg->type==INT)
	{
		on = arg->iVar;
		ioctl(led_fd, on, led_no);
		return 1;
	}
	
	return 0;
}


Hardware* Create_LED1()
{
	LED1.Open = LED1_Open;
	LED1.Init= LED1_Init;
	LED1.Close= LED1_Close;
	LED1.NVALUE_ParserCallback = LED1_NVALUE_ParserCallbackImpl;
	return &LED1;
}







stuoutsub.c改后的代码:

/*******************************************************************************
 * Copyright (c) 2012, 2013 IBM Corp.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution. 
 *
 * The Eclipse Public License is available at 
 *   http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *    Ian Craggs - initial contribution
 *    Ian Craggs - change delimiter option from char to string
 *    Al Stockdill-Mander - Version using the embedded C client
 *******************************************************************************/

/*
 
 stdout subscriber
 
 compulsory parameters:
 
  topic to subscribe to
 
 defaulted parameters:
 
	--host localhost
	--port 1883
	--qos 2
	--delimiter \n
	--clientid stdout_subscriber
	
	--userid none
	--password none

 for example:

    stdoutsub topic/of/interest --host iot.eclipse.org

*/
#include <stdio.h>
#include "MQTTClient.h"

#include <stdio.h>
#include <signal.h>
#include <memory.h>

#include <sys/time.h>


//================== Added 2017-Apr-16 16:57:21 start ==================
#include <sys/ioctl.h>
#include "DomoticzMessageParser.h"
#include "LED0.h"
#include "LED1.h"
//================== Added 2017-Apr-16 16:57:21  end ===================

volatile int toStop = 0;

#include <stdarg.h>
#define dprintf(msg,args...)  printf("%s,line:%d,"msg,__FILE__,__LINE__,##args)

void usage()
{
	printf("MQTT stdout subscriber\n");
	printf("Usage: stdoutsub topicname <options>, where options are:\n");
	printf("  --host <hostname> (default is localhost)\n");
	printf("  --port <port> (default is 1883)\n");
	printf("  --qos <qos> (default is 2)\n");
	printf("  --delimiter <delim> (default is \\n)\n");
	printf("  --clientid <clientid> (default is hostname+timestamp)\n");
	printf("  --username none\n");
	printf("  --password none\n");
	printf("  --showtopics <on or off> (default is on if the topic has a wildcard, else off)\n");
	exit(-1);
}


void cfinish(int sig)
{
	signal(SIGINT, NULL);
	toStop = 1;
}


struct opts_struct
{
	char* clientid;
	int nodelimiter;
	char* delimiter;
	enum QoS qos;
	char* username;
	char* password;
	char* host;
	int port;
	int showtopics;
} opts =
{
	(char*)"stdout-subscriber", 0, (char*)"\n", QOS2, NULL, NULL, (char*)"localhost", 1883, 0
};


void getopts(int argc, char** argv)
{
	int count = 2;
	
	while (count < argc)
	{
		if (strcmp(argv[count], "--qos") == 0)
		{
			if (++count < argc)
			{
				if (strcmp(argv[count], "0") == 0)
					opts.qos = QOS0;
				else if (strcmp(argv[count], "1") == 0)
					opts.qos = QOS1;
				else if (strcmp(argv[count], "2") == 0)
					opts.qos = QOS2;
				else
					usage();
			}
			else
				usage();
		}
		else if (strcmp(argv[count], "--host") == 0)
		{
			if (++count < argc)
				opts.host = argv[count];
			else
				usage();
		}
		else if (strcmp(argv[count], "--port") == 0)
		{
			if (++count < argc)
				opts.port = atoi(argv[count]);
			else
				usage();
		}
		else if (strcmp(argv[count], "--clientid") == 0)
		{
			if (++count < argc)
				opts.clientid = argv[count];
			else
				usage();
		}
		else if (strcmp(argv[count], "--username") == 0)
		{
			if (++count < argc)
				opts.username = argv[count];
			else
				usage();
		}
		else if (strcmp(argv[count], "--password") == 0)
		{
			if (++count < argc)
				opts.password = argv[count];
			else
				usage();
		}
		else if (strcmp(argv[count], "--delimiter") == 0)
		{
			if (++count < argc)
				opts.delimiter = argv[count];
			else
				opts.nodelimiter = 1;
		}
		else if (strcmp(argv[count], "--showtopics") == 0)
		{
			if (++count < argc)
			{
				if (strcmp(argv[count], "on") == 0)
					opts.showtopics = 1;
				else if (strcmp(argv[count], "off") == 0)
					opts.showtopics = 0;
				else
					usage();
			}
			else
				usage();
		}
		count++;
	}
	
}

void messageArrived(MessageData* md)
{
	MQTTMessage* message = md->message;
	if (opts.showtopics)
		printf("%.*s\t", md->topicName->lenstring.len, md->topicName->lenstring.data);
	if (opts.nodelimiter)
		printf("%.*s", (int)message->payloadlen, (char*)message->payload);
	else
		printf("%.*s%s", (int)message->payloadlen, (char*)message->payload, opts.delimiter);
	//fflush(stdout);
//================== Added 2017-Apr-16 16:56:59 start ==================
	ParseDomoticzMessage((char*)message->payload);
//================== Added 2017-Apr-16 16:56:59  end ===================
}

int led_fd;

int main(int argc, char** argv)
{	
	int rc = 0;
	unsigned char buf[100];
	unsigned char readbuf[100];
	
	if (argc < 2)
		usage();
	
	char* topic = argv[1];

	if (strchr(topic, '#') || strchr(topic, '+'))
		opts.showtopics = 1;
	if (opts.showtopics)
		printf("topic is %s\n", topic);

	getopts(argc, argv);	
	
	Network n;
	//Client c;
	MQTTClient c;

	signal(SIGINT, cfinish);
	signal(SIGTERM, cfinish);
	
//================== Added 2017-Apr-16 16:56:44 start ==================
	led_fd = open("/dev/leds0", 0);
	if (led_fd < 0) {
		led_fd = open("/dev/leds", 0);
	}
	if (led_fd < 0) {
		perror("open device leds");
		exit(1);
	}
	RegisterHaraware(Create_LED0(),1);
	RegisterHaraware(Create_LED1(),2);
	OpenHardwares();
	SetupDomoitczMessageParser();	
	SetEnableParseItem(KEY_IDX);
	SetEnableParseItem(KEY_NVALUE);
	SetEnableParseItem(KEY_SWITCH_TYPE);
	
	initHardwareSettings();
//================== Added 2017-Apr-16 16:56:44  end ===================
	
	NetworkInit(&n);
	NetworkConnect(&n, opts.host, opts.port);
	MQTTClientInit(&c, &n, 1000, buf, 100, readbuf, 100);

	MQTTPacket_connectData data = MQTTPacket_connectData_initializer;       
	data.willFlag = 0;
	data.MQTTVersion = 3;
	data.clientID.cstring = opts.clientid;
	data.username.cstring = opts.username;
	data.password.cstring = opts.password;

	data.keepAliveInterval = 10;
	data.cleansession = 1;
	printf("Connecting to %s %d\n", opts.host, opts.port);
	
	rc = MQTTConnect(&c, &data);
	printf("Connected %d\n", rc);
    printf("Subscribing to %s\n", topic);
	rc = MQTTSubscribe(&c, topic, opts.qos, messageArrived);
	printf("Subscribed %d\n", rc);

	while (!toStop)
	{
		MQTTYield(&c, 1000);	
	}
	
	printf("Stopping\n");

	MQTTDisconnect(&c);
	NetworkDisconnect(&n);	

//================== Added 2017-Apr-16 16:57:50 start ==================
	CloseHardwares();
	close(led_fd);
	exit(0);
//================== Added 2017-Apr-16 16:57:50  end ===================	
	return 0;
}





Makefile:


TOPDIR = ../..

CC:=arm-linux-gcc

INCDIR :=-I/usr/local/arm/paho.mqtt.embedded-c/include

COMPILE.c   = $(CC)  $(CFLAGS) $(INCDIR) -MMD -c
LINK.c      = $(CC)  $(LDFLAGS) -lpthread -lrt 
.PHONY: all
%.o:%.c
	$(COMPILE.c) $< -o $@

SRC_FILE := \
	MQTTClient.c \
	MQTTLinux.c \
	DomoticzMessageParser.c \
	HardwareControl.c \
	LED0.c \
	LED1.c \
	stdoutsub.c
OBJS := $(addsuffix .o, $(basename $(SRC_FILE)))
DEPS :=$(OBJS:.o=.d)

all:mqtt

mqtt:$(OBJS)
	$(LINK.c) $(OBJS) -lpaho-embed-mqtt3c -L/usr/local/arm/paho.mqtt.embedded-c/lib  -o $@

install:
	cp mqtt /work/rootfs/usr/my/

clean:
	rm -f $(OBJS) mqtt

distclean:clean
	rm -f $(DEPS)


-include $(DEPS)


然后执行:

make

make install


可执行文件已经拷贝到NFS的目标板目录上去了。


实现的效果跟前一篇文章基本上是一样的,只不过用了一个简单框架,来提高扩展性和灵活性,就不贴实际效果图了。

这个框架的代码在GCC上编译是OK,理论上支持C99的编译器都应该没问题其他编译器没有试过

框架本身理论上可以用在其他嵌入式操作系统上,不过没试过。

用它来做个探索,方便后续的STM32平台的开发。



猜你喜欢

转载自blog.csdn.net/sqshining/article/details/70201370