本帖最后由 shuxulala 于 2015-7-8 13:29 编辑
如题,为了方便大家实现批量设备的快速创建,在此教大家在设备终端上运用RestFul API实现设备的批量创建,下面来看看本次实验用到的设备终端,如下图,本次实验用到的是一款STM32F103的简易开发板,前段时间有坛友用过,该开发板提供USB(USB模拟串口)、以太网口、JTAG等接口。
下面看看代码实现,本实例代码是在UCOSII下开发的,运用Lwip实现网络通信,由于代码比较多,这里只贴出主要流程代码,重点展示运用RestFul API实现平台设备批量创建的过程和原理,对于设备硬件设置和初始化、Lwip移植不做详细说明,首先看看几个重要的宏定义和变量:
#define DEV_NAME "My_Dev" //设备名
#define DEV_CNT 10 //要创建的设备数量
#define API_KEY "your ApiKey" //ApiKey
#define SERVER_ADDR "api.heclouds.com" //服务器地址
#define SERVER_PORT 80 //服务器端口号
unsigned int sys_timer = 0; //系统时间Tick
char send_buf[512]; //HTTP数据包存储buff
unsigned int dev_num = 0; //设备计数复制代码
入口函数:
int main(void)
{
CPU_INT08U os_err;
/* 禁止所有中断 */
CPU_IntDis();
/* ucosII 初始化 */
OSInit();
/* 硬件平台初始化 */
BSP_Init();
/*初始化ucosII时钟节拍*/
OS_CPU_SysTickInit();
/*初始化Lwip*/
lwip_init_task();
/*建立主任务,该任务执行函数为App_TaskStart */
os_err = OSTaskCreate((void (*)(void *)) App_TaskStart, //指向任务代码的指针
(void *) 0, //任务开始执行时,传递给任务的参数的指针
(OS_STK *) &App_TaskStartStk[APP_TASK_START_STK_SIZE - 1],
(INT8U) 1); //分配给任务的优先级
OSTimeSet(0); //ucosII的节拍计数器清0 节拍计数器是0-4294967295
OSStart(); //启动ucosII内核
return 0;
}复制代码
主函数创建一个任务,该任务的执行入口函数为App_TaskStart,下面我们看该函数的实现:
void App_TaskStart(void* p_arg)
{
unsigned int count = 0;
/*创建新线程执行数据上传任务,线程的执行入口函数mkdev_demo*/
sys_thread_new("mkdev_demo", mkdev_demo, 0, 512, TCPIP_THREAD_PRIO + 1);
while(1)
{
/*use process_mac() to query netif packages*/
process_mac();
count++;
if(count % 10 == 0)
{
/*给其他任务留出执行时间*/
OSTimeDly(1);
}
}
}复制代码
App_TaskStart最主要的作用是新建了一个线程,该线程的执行入口为mkdev_demo函数,具体实现如下:
void mkdev_demo(void * arg)
{复制代码
int sockfd, ret;
EdpPacket* send_pkg;
/*初始化内存*/
uMEM_Init();
sys_timer = sys_now();
while(1)
{
/* 与服务器创建TCP连接 */
sockfd = Open(SERVER_ADDR, SERVER_PORT);
if(sockfd < 0)
{
Close(sockfd);
continue;
}
/*发送数据包和接收服务器响应*/
client_process_func(&sockfd);
Close(sockfd);
}复制代码
}
mkdev_demo首先初始化内存,和服务器创建TCP连接,最后调用client_process_func发送HTTP数据包并接收来自服务器的响应信息,client_process_func函数实现如下:
int client_process_func(void* arg)
{
int sockfd = *(int*)arg;
fd_set readset;
fd_set writeset;
int i, maxfdp1;
for(;;)
{
maxfdp1 = sockfd+1;
/*套节字集合清空 */
FD_ZERO(&readset);
FD_ZERO(&writeset);
/*加入读写套接字集合*/
FD_SET(sockfd, &readset);
FD_SET(sockfd, &writeset);
/*检测套接字是否可读可写*/
i = select(maxfdp1, &readset, &writeset, 0, 0);
/*套接字不可读不可写*/
if(i == 0)
continue;
/*检测sockfd是否在读套接字集合里面*/
if(FD_ISSET(sockfd, &readset))
{
/*如果可读,接收网络数据*/
if(recv_func(sockfd) < 0)
break;
}
/*如果socket 准备好可写,调用write_func函数发送数据到远程终端*/
if(FD_ISSET(sockfd, &writeset))
{
if(write_func(sockfd) < 0)
break;
}
}
return -1;
}复制代码
client_process_func函数实现socket的读写监听设置,当检测到有网络数据的时候调用recv_func接收数据,当检测到可以发送数据的时候调用write_func发送创建设备的数据包,write_func实现如下:
int write_func(int arg)
{
int sockfd = arg;
unsigned int now = sys_now();
int32 ret = 0;
char *pJson;
char tmp[50];
/*控制发送HTTP包间隔,如果检测到设备已经创建设备个数已经达到设置值,不再创建*/
if(now - sys_timer < 2000 && dev_num < DEV_CNT)
{
return 0;
}
if(dev_num < DEV_CNT)
{
sys_timer = now;
/*创建HTTP请求内容,即描述创建设备相关信息的JSON串*/
pJson = makeJson();
/*HTTP包头封装*/
send_buf[0] = 0;
strcat(send_buf,"POST /devices HTTP/1.1\r\n");
strcat(send_buf,"api-key:");
strcat(send_buf,API_KEY);
strcat(send_buf,"\r\n");
strcat(send_buf,"Host:");
strcat(send_buf,SERVER_ADDR);
strcat(send_buf,"\r\n");
sprintf(tmp,"Content-Length:%d\r\n\r\n", strlen(pJson));
strcat(send_buf,tmp);
strcat(send_buf,pJson);
/*释放产生JSON字符串所分配的内存*/
uMEM_Free(pJson);
/*发送数据包*/
ret = DoSend(sockfd, send_buf, strlen(send_buf));
/*创建设备计数*/
dev_num ++;
return ret;
}
}复制代码
函数write_func实现HTTP包的封装并发送,其中makeJson创建HTTP包中的HTTP请求内容,makeJson中主要调用JSON的C语言库函数进行数据封装:
char* makeJson(void)
{
cJSON * pJsonRoot = NULL;
char buff[50] = {0};
char * p = NULL;
cJSON * pSubJson = NULL;
/*创建JSON对象*/
pJsonRoot = cJSON_CreateObject();
if(NULL == pJsonRoot)
{
return NULL;
}
sprintf(buff, "%s%d", DEV_NAME,dev_num);
/*添加JSON对象中设备相关的描述信息,设备的名字*/
cJSON_AddStringToObject(pJsonRoot, "title", buff);
/*添加JSON对象中设备相关的描述信息,设备的描述信息*/
cJSON_AddStringToObject(pJsonRoot, "desc", "my device test");
/*JSON子对象*/
pSubJson = cJSON_CreateObject();
if(NULL == pSubJson)
{
cJSON_Delete(pJsonRoot);
return NULL;
}
/*添加设备的地图位置信息*/
cJSON_AddNumberToObject(pSubJson, "ele", 37000);
cJSON_AddNumberToObject(pSubJson, "lat", 17.609997);
cJSON_AddNumberToObject(pSubJson, "lon", 177.03403);
/*子对象添加到跟对象中*/
cJSON_AddItemToObject(pJsonRoot, "location", pSubJson);
/*设备的属性,私有设备*/
cJSON_AddBoolToObject(pJsonRoot, "private", 1);
/*添加设备的默认路由信息*/
cJSON_AddStringToObject(pJsonRoot, "route_to", "2113");
/*分配内存并输出JSON串到内存中*/
p = cJSON_Print(pJsonRoot);
if(NULL == p)
{
cJSON_Delete(pJsonRoot);
return NULL;
}
cJSON_Delete(pJsonRoot);
/*返回JSON字符串指针,该指针指向的内存需要在使用完JSON串后释放*/
return p;
}复制代码
makeJson函数实现HTTP请求内容的封装,封装后输出的信息格式类似于:
{
"title":"My_Dev0",//用户范围内唯一
"desc":"my device test",
"location":{//可选
"ele":370000,
"lat":17,
"lon":177
},复制代码
"private":true,
"route_to":"2113",//socket接口发送数据的默认路由地址(设备ID号)
}
recv_func函数主要作用是接收服务的响应信息:
int recv_func(int arg)
{
int sockfd = arg;
int n, rtn, error;
int ret = 0;
do
{
/*接收网络数据*/
n = Recv(sockfd, buffer, 512, 0);
if(n <= 0)
{
Printf("recv error, bytes: %d\n", n);
error = -1;
break;
}
/*打印网络数据*/
Printf("recv from server, bytes: %d\n", n);
Printf("*****return result******\n%s\n\n", buffer);
}
while(0);
return error;
}复制代码
以上就是在设备终端上运用RestFul API实现平台设备批量创建的全过程,将代码编译下载(借助JTAG)到设备终端,运行,可以在平台的设备管理页面查看设备的批量创建具体情况,例如,我们使用以上代码在平台相应账户下批量创建10个设备,设备管理界面显示如下:
可以看到,我们在平台的响应账户下创建了名为My_DevX的10个设备(X表示设备的创建序号0-9),设备批量创建成功。与之类似,运用RestFul API的其他接口可以实现设备资源的批量操作。