前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >erpc(EmbeddedRPC)入门笔记

erpc(EmbeddedRPC)入门笔记

作者头像
10km
发布2020-04-24 14:48:52
4.5K0
发布2020-04-24 14:48:52
举报
文章被收录于专栏:10km的专栏10km的专栏

RPC

最近在忙一个IOT设备的项目,想设计一个通信系统通过串口控制设备(freertos)的运行。按照传统的设计思路,先要定义一套串口通信协议,在这套协议中传输层协议、应用层协议一个都不能少。每一层协议都要自己实现。数据编码/解码,数据校验,容错,这些非常基础的东西都要自己实现。 等这些协议都实现了,才是能开始设计真正的业务逻辑。 和同事商议后,一致认为要是照这么干,黄花菜都凉了。我们的生命不能浪费在这些无意义的劳动上! 我想到了RPC概念是适用于我们的应用场景的。实际我们就是在串口上实现一个客户端请求->服务端响应的模型。除了传输层是串行通信,这与我们一般在tcp/ip网络上常见的client/server模型没啥区别,就是1对1简化版的client/server模型。比如也许google的基于protocol bufffers的grpc就能满足要求。如果能利用现成的开发框架,可以大大减化开发流程,减少开发时间。

定义下了RPC这个开发方向后我和同事分头去网上找相关资料,进行开发框架的选型调研。最终同事找到了恩智浦(NXP)的开源项目erpc(EmbeddedRPC)。看到这个项目说明,我感觉它就是为我们量身定做的。

https://github.com/EmbeddedRPC/erpc

eRPC (Embedded RPC) is an open source Remote Procedure Call (RPC) system for multichip embedded systems and heterogeneous multicore SoCs. eRPC (embeddedreprocedure-Call)是一个面向多芯片嵌入式系统和异构多核soc的开源RPC框架。

哇哦,与grpc面向通用tcp网络不同,erpc就是面向嵌入式系统设计的。体积还很小(根据官方介绍可以小到5KB)。完美,下面的事情就是入门学习了。

erpc目前支持的传输层如下,除了我不认识的协议外,主要就是串口通信和TCP/IP了,TCP/IP主要用于测试:

Supported transports: CMSIS UART NXP Kinetis SPI and DSPI POSIX and Windows serial port TCP/IP (mostly for testing) NXP RPMsg-Lite

也就是说我们可以在不依赖具体设备的情况通过使用TCP/IP传输层模拟串口,就可以x86平台的电脑上实现RPC调用的两端(client/server)的模拟通信了。采用这方式无疑可以大大提高开发效率–不需要所有的测试都在具备硬件设备上运行,PC模拟可以快捷方便的完成很多事件。

于是我们重新开始在ubuntu 16.04下开始了erpc搭建通信框架的过程

编译准备

安装依赖库

代码语言:javascript
复制
	# install flex & bison
	sudo apt-get install flex bison
	# install boost
	sudo apt-get install libboost-dev libboost-system-dev libboost-filesystem-dev

下载erpc

克隆erpc 项目到本地(erpc文件夹)

代码语言:javascript
复制
git clone https://github.com/EmbeddedRPC/erpc.git

erpcgen : IDL编译器

erpcgen是IDL编译器,用于将.erpc后缀的接口定义(IDL)文件生成对应的client/server代码。 进入erpc/erpcgen子项目编译erpcgen并安装到/usr/local/bin下:

代码语言:javascript
复制
cd erpc/erpcgen
make -j8
sudo make install 

NOTE: -j8 为并行编译选项,指定使用8个线程同时编译,以加快编译速度

eprc : erpc核心库

erpc/erpc_c下是erpc的核心库,需要编译供后续项目使用

代码语言:javascript
复制
cd erpc/erpcgen
make -j8
# 默认安装到 /usr/local/include/erpc /usr/local/lib
sudo make install 

定义IDL

上面的一切都准备就绪了,接口定义就是我们最关注的业务逻辑了,如下定义一个简单的IDL,这个接口中只有一个函数: erpcdemo.erpc

代码语言:javascript
复制
/*! 定义项目名称,也是所有生成的源码文件名前缀 */
program erpcdemo

/*! 定义返回状态枚举类型 */
enum lockErrors_t
{
  lErrorOk_c = 0,
  lErrorOutofMemory_c,
  // ......定义状态码
  // 最大枚举类型值
  lErrorMaxError_c
}

/*! 定义服务接口函数 */
interface DEMO {
    RD_demoHello(binary txInput) -> binary
}

IDL语法参见erpc官方文档:

《IDL Reference(https://github.com/EmbeddedRPC/erpc/wiki/IDL-Reference)》

生成代码

根据接口定义文件erpcdemo.erpc生成对应的client/server代码

代码语言:javascript
复制
erpcgen erpcdemo.erpc

NOTE: 事前必须先执行erpcgen编译安装。

生成的文件列表:

代码语言:javascript
复制
erpcdemo.h
erpcdemo_client.cpp
erpcdemo_server.cpp
erpcdemo_server.h

Client测试程序

基于上述生成的代码可以很简单的写出client端测试程序

代码语言:javascript
复制
/*
 * test_erpcdemo_client.c
 *
 *  Created on: Apr 14, 2020
 *      Author: guyadong
 */


#include <string.h>
#include <iostream>
#include <erpc_client_setup.h>
#include <erpc_port.h>
#include "erpcdemo.h"
#include "erpc_setup_tcp.h"
using namespace std;
// 释放binary占用的空间
static void free_binary_t_struct(binary_t * data)
{
    if (data->data)
    {
        erpc_free(data->data);
    }
}
int main(int argc, char *argv[])
{
	/* 创建client端传输层对象(TCP),127.0.0.1:5407 */
	auto transport = erpc_transport_tcp_init("127.0.0.1",5407, false);
	auto message_buffer_factory = erpc_mbf_dynamic_init();
	/* 初始化客户端 */
	erpc_client_init(transport,message_buffer_factory);
	auto msg = "hello!!!!!";
	binary_t b = {(uint8_t*)msg,(uint32_t)strlen(msg)};
	{
		/* RPC 调用 */
		auto resp = RD_demoHello(&b);
		/* 输出返回值 */
		cout << "RD_demoHello response:" << resp->data << endl;
		/* 对于返回指针类型的数据,用完后需要释放RD_demoHello中分配的内存 */
		free_binary_t_struct(resp);
	}
	/* 关闭socket */
	erpc_transport_tcp_deinit();
}

Server端测试程序

基于上述生成的代码可以很简单的写出server端测试程序,与client测试程序不同的是,server端要提供接口函数的具体实现

代码语言:javascript
复制
/*
 * test_erpcdemo_server.cpp
 *
 *  Created on: Apr 15, 2020
 *      Author: guyadong
 */

#include <iostream>
#include <time.h>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <string.h>
#include <erpc_server_setup.h>
#include "erpcdemo_server.h"
#include "erpc_setup_tcp.h"
using namespace std;
/** 返回当前时间字符串 */
static std::string now_str() {
	time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
	std::stringstream ss;
	ss << std::put_time(std::localtime(&t), "%F %T");
	return ss.str();
}
/** servicer端实现接口方法 */
binary_t * RD_demoHello(const binary_t * txInput){
	cout << "RD_demoHello called" << endl;
	string o ((char*)txInput->data);
	o.append("@").append(now_str());
	auto ol = strlen(o.c_str());
	char* buf = (char*)malloc(ol + 1);
	strncpy(buf,o.c_str(),ol);
	return new binary_t{(uint8_t*)buf,(uint32_t)ol};
}
int main(int argc, char *argv[]){
	// 创建client端传输层对象(TCP),127.0.0.1:5407
	auto transport = erpc_transport_tcp_init("127.0.0.1",5407, true);
	/* MessageBufferFactory initialization */
	erpc_mbf_t message_buffer_factory = erpc_mbf_dynamic_init();
	/* eRPC 服务端初始化 */
	erpc_server_init(transport, message_buffer_factory);
	/** 将生成的接口服务DEMO添加到server, 参见生成的源文件 erpcdemo_server.h */
	erpc_add_service_to_server(create_DEMO_service());
	cout << "start erpcdemo server" << endl;
	/* 启动服务器 */
	erpc_server_run(); /* or erpc_server_poll(); */
	/* 关闭socket */
	erpc_transport_tcp_deinit();
	return 0;
}

分别编译 test_erpcdemo_server.cpp,test_erpcdemo_client.cpp,我们就有了在linux下运行的一个最简单的基于erpc的RPC演示系统。

在这里插入图片描述
在这里插入图片描述

NOTE: 如果你会用cygwin,就不必要在ubuntu下执行,可以在windows平台 cygwin terminal下执行上述所有过程。上面的截图就是windows下的cygwin 终端执行的效果

总结

在上面的过程中,涉及数据传输,序列化,反序列,校验等等底层的细节都由erpc完成了。我们只是关注于定义业务接口本身,确实方便了好多啊。

完整代码

本文所涉及的所有源码的完整代码及详细说明参见码云仓库:

https://gitee.com/l0km/erpcdemo.git

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • RPC
  • 编译准备
  • 下载erpc
  • erpcgen : IDL编译器
  • eprc : erpc核心库
  • 定义IDL
  • 生成代码
  • Client测试程序
  • Server端测试程序
  • 总结
  • 完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档