ffxz 发表于 2013-6-22 22:41:39

RT-Thread,机器间通信

好像挺长时间没来这边了,过来转转,也继续和大家交流RT-Thread的情况

这个月底就要发布RT-Thread 1.2.0 beta了,貌似1.2.0稍微有些偏离原来设定的目标(注:原来设定的目标是文档),反而花了很多力气时间在机器间互联上面。

例如RT-Thread 1.2.0 beta版本将默认使用lwIP 1.4.1的版本,而lwIP 1.4.0在长时间高负荷通信测试时依然会存在些问题,1.4.1则修正掉了。1.4.0的PPP也存在问题的,1.4.1相应的也修正了。应了一句话,主流的情况下,lwIP是没问题的,其他的还是需要自己仔细多多测试。

谈到机器间通信,火热些就是物联网,规矩些就是分布式运算,俗气些就是两个芯片间相互发发消息。

所以简单的就是,两个芯片通过UART、SPI、IIC,甚至是以太网,GPRS,WIFI,BLE连起来,然后相互发一个hello。

不过嵌入式设备还是C语言用得比较多,相互发消息就得写C代码(所以RT-Thread的finsh shell用的就是C代码的语法表达式解析),如果说可以做到下面的,这就挺好了:

Machine #1
void my_thread(void*)
{
    send("hello");
    rt_kprintf("%s\n", recv());
}

Machine #2
void my_thread(void*)
{
    if (strcmp(recv(), "hello") == 0)
    {
          send("hi");
    }
}

不过这样的问题微观些,显然很糟糕,发过去对方不响应怎么办,发过去对方乱发东西怎么办。接收这边,直接接收过来,memory谁来处理?

因为Machine上运行的是代码,所以要正确通信,需要写一个正确,规范的代码给Machine,否则对方如何正确理会呢。

代码还是代码,既然Machine上主要还是使用C代码,那么我们就基于C代码定义些规则。

Rule #1
Machine与Machine直接是进行数据交互,翻译成C代码就是,C的结构体,例如:
struct foo
{
    char msg;
    int code;
};

Machine #1发送一个struct foo给Machine #2。当然Machine #2也可以回复另外一个结构体,例如:
struct result
{
    int code;
};

Rule #2
Machine与Machine之间应该发送的数据,而不是指针。
为什么不是指针,因为指针指向的数据肯定是本地的,把指针发过去肯定不靠谱。

Rule #3
Machine与Machine之间可以通过数据交互完成大部分操作,例如远程RPC:
struct rpc_request
{
    char proc;
    int argc;
    int argv;
};

struct rpc_result
{
    int rc;
};

在struct rpc_request::proc中给出远端要执行的函数,然后argc是参数的个数,argv是参数(但这里仅局限于整数)。struct rpc_result给出了执行这个函数后的返回值。

显然当一个函数复杂时(各类参数,参数返回值等),这样是行不通的!

为了简化问题,我们先暂时继续这个rpc_request/rpc_result。

Machine与Machine之间既然是基于C结构体交互的,那么我们直接对C结构体进行处理不就可以么?!但是选择二进制交互吗?如果是跨架构,跨语言,跨越云端呢?现在web好像蛮普遍吧,web里有个东西,json (http://www.json.org)。例如一个struct rpc_request可以用这个json来表示:
{"proc":"list_thread", "argc":0, "argv":}
对应的结果struct rpc_result可以表示成:
{"rc":0}

完全基于文本方式进行,挺简单明了的,读起来容易懂。

我们把上面的rpc结构体变成一个头文件:
#ifndef __RPC_H__
#define __RPC_H__

#include <mjson.h>

struct rpc_request
{
    char proc;
    int argc;
    int argv;
};
JSON_EXPORT(rpc_request);

struct rpc_result
{
    int rc;
};
JSON_EXPORT(rpc_result);

#endif

是否可以有一个程序把头文件中的结构体,生成出转换这些结构体到json的函数?例如说,rpc_request_decode_json/rpc_request_encode_json.这样当要进行交换结构体时,可以由底层自动做掉。例如生成的代码类似这样:
/* This file is generated by mjsonc. Do not edit. */
#include "rpc_mjson.h"
#include <stdio.h>

#ifdef WIN32
#define snprintf _snprintf
#endif

rt_size_t rpc_request_encode_json(struct rpc_request* self, rt_uint8_t* buf, rt_size_t size)
{
        uint32_t index;
        rt_uint8_t *ptr = buf;

        ptr += snprintf((char*)ptr, size - (ptr - buf), "{");
        ptr += snprintf((char*)ptr, size - (ptr - buf), "\"proc\":\"%.*s\"", 32, self->proc);
        ptr += snprintf((char*)ptr, size - (ptr - buf), ", \"argv\":[");
        ptr += snprintf((char*)ptr, size - (ptr - buf), "%d", self->argv);
        for (index = 1; index < 8; index ++)
        {
                ptr += snprintf((char*)ptr, size - (ptr - buf), ", %d", self->argv);
        }
        ptr += snprintf((char*)ptr, size - (ptr - buf), "]");
        ptr += snprintf((char*)ptr, size - (ptr - buf), ",\"argc\": %d", self->argc);
        ptr += snprintf((char*)ptr, size - (ptr - buf), "}");

        return ptr - buf;
}

rt_size_t rpc_result_encode_json(struct rpc_result* self, rt_uint8_t* buf, rt_size_t size)
{
        rt_uint8_t *ptr = buf;

        ptr += snprintf((char*)ptr, size - (ptr - buf), "{");
        ptr += snprintf((char*)ptr, size - (ptr - buf), "\"rc\": %d", self->rc);
        ptr += snprintf((char*)ptr, size - (ptr - buf), "}");

        return ptr - buf;
}

void rpc_request_decode_json_with_node(struct rpc_request* self, struct json_node* node)
{
        uint32_t index;
        struct json_node* child;

        snprintf(self->proc, 32, "%.*s", 32, json_node_get_string(node, "proc", RT_NULL));
        child = json_get_node(node, "argv", RT_NULL);
        for (index = 0; index < 8; index ++)
                self->argv = json_node_get_integer(json_node_get_array(child, index, RT_NULL), RT_NULL);
        self->argc = json_node_get_integer(node, "argc", RT_NULL);
}
MJSON_DECODE_DEFINE(rpc_request);

void rpc_result_decode_json_with_node(struct rpc_result* self, struct json_node* node)
{
        self->rc = json_node_get_integer(node, "rc", RT_NULL);
}
MJSON_DECODE_DEFINE(rpc_result);

假设和远程的服务端进行交互,可以按照类似下面的代码来调用:
void mjson_http(void)
{
        struct rpc_request request;
        struct rpc_result result;

      /* request .... */
      memset(&request, 0x0, sizeof(struct rpc_request));
      strcpy(request.proc, "list_thread");

       /* 假设服务端访问链接是http://192.168.10.10/json.php */
        mjson_do_http("http://192.168.10.10/json.php", "rpc_request", &request,
                "rpc_result", &result);

        rt_kprintf("result: %d\n", result.rc);
}

总结下,当要进行Machine与Machine间交互时,需要定义个C结构,如果是http方式,调用
mjson_do_http(URI, "request", &request, "result", &result);
来进行自动交互数据。

如果是UART,则可以是mjson_do_uart,其它的类推(例如IIC,SPI等)。当然也可以是:mjson_do(URI, ....);具体的传输方式由URI来指定。

Machine可以是执行RT-Thread的终端,也可以是运行web服务器的超强server,或者是手机、平板、电视。反正Machine与Machine之间通过json进行数据沟通,C结构体自动通过mjsonc编译器生成相应的json编码和解码函数。

nongxiaoming 发表于 2013-6-22 22:47:08

沙发啊,这个爽

nongxiaoming 发表于 2013-6-22 22:55:45

mjson做这个还真是方便多了

aozima 发表于 2013-6-22 23:04:14

本帖最后由 aozima 于 2013-6-22 23:05 编辑

占位学习。

json在浏览器上面用得非常多,这样的话板子与浏览器页面的交互也方便了。

geniuscode 发表于 2013-6-23 00:00:11

{:tongue:}让我想起当年做字幕文件解析了!!!!{:titter:}{:titter:}{:titter:}{:titter:}

ffxz 发表于 2013-6-23 08:14:12

下个周末我会在北京讲这个机器间通信,蛮有意思的一个东西。

这个难点主要在mjsonc上,全称是M2M json compiler,即它对C的头文件进行预处理、词法分析、语法分析,然后再生成对应的json编码、解码函数。这样做的好处是内存空间占用大大降低,因为mjson编译器都帮你做了啊

eye 发表于 2013-6-28 00:57:03

Restful API 的server 和client

eye 发表于 2013-6-28 01:14:13

倒过来实现似乎会简单一些,
1. JSON文件配置好API接口
2. 利用现成的模板引擎(例如Jinja2)生成API的.h接口,.c解析引擎, 还有对应API的回调函数由使用者去具体实现.
3. 实现对应的API服务函数.

mjsonc就是一个(几个)模板文件, 规范配置文件的JSON格式, Python解析就直接出C代码了. 甚至可以做成在线版本, 开发者都不需要Python环境.

feiwa 发表于 2013-6-29 05:06:36

好,跟着学习

migrant 发表于 2013-6-29 09:45:40

RT-Thread做得真不错

lixun00 发表于 2013-6-29 16:17:36

关注,这个真的非常方便!

hottest_boy 发表于 2013-7-1 13:57:01

关注这个,改天再好好看看,M2M是一种必然……

ffxz 发表于 2013-7-4 07:00:16

是的,M2M是一种必然,我们后续会给出一些更详细的demo,包括软硬件,网络服务端。。。
页: [1]
查看完整版本: RT-Thread,机器间通信