您的位置:首页 > 运维架构 > Linux

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

2017-04-17 00:45 976 查看
为了充分利用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
******************************************************************************/
#ifnd
4000
ef 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->p
f4e4
arseArg));
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平台的开发。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐