克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

EmbedXrpc 用户手册

QQ群134161401

这个东西类似于Google的GRPC,但是应用场景是单片机。RPC远程调用极大的方便了开发,使得不必关注于协议解析,数据的序列化和反序列化等繁琐的工作。可是目前还没有在单片机上实现好用的RPC框架,于是我就谋生了做这个RPC框架的想法,所用的技术是:C#做 IDL语言+csscript+自己实现序列化和反序列化+代码生成(Razor Engine) QQ群134161401

应用场景:单片机近距离Client/Server交互场景(USB、串口、CAN(如J1939 、ISO15765协议等),)只要是流协议都支持

Sample1工程是例子!除了main.cpp的代码是手工写的之外,其他的代码都是工具生成的!这是个最简单的例子!

编译此工程你需要安装Qt5.14.2和Qt For Vs Addin插件。

此工程演示了:

1.客户端每一秒向服务端发送1、2 服务端计算出来3后,把3发送给客户端

2.服务端每1秒广播当前的时间,客户端打印到控制台上

编译EmbedXrpc代码生成器EXE

1.用vs2019 打开EmbedXrpcIdlParser.sln编译好(编译EXE只做一次)。 2.在EmbedXrpcIdlParser\bin\Debug会出现一个EmbedXrpcIdlParser.exe,

用户使用步骤(以idltest1.cs为例子)

1.编写你的IDL文件,目录下会有一个idltest1.cs 文件,是例子,你可以参考一下。 2.执行命令

.\EmbedXrpcIdlParser.exe -g all -l cpp -i idltest1.cs -o yourPath
-g 代表生成client还是server 还是all
-l 代表生成cpp还是 cs
-o 代表生成的代码打算放到哪个目录

5.生成好代码后(我假定你生成在了A文件夹下,

其中:
A文件夹所有的文件(包括子目录下的) + EmbedXrpcRuntime/Cpp + EmbedXrpcRuntime/Cpp/Win32.Port = 客户端(上位机端)的源文件(如果上位机用C++开发)
A文件夹所有的文件(包括子目录下的) + EmbedXrpcRuntime/CS = 客户端(上位机端)的源文件(如果上位机用C#开发)
A文件夹所有的文件(包括子目录下的) + EmbedXrpcRuntime/Cpp + EmbedXrpcRuntime/Cpp/RT-Thread.Port = 服务端(单片机端)的源文件 RT-Thread的移植
A文件夹所有的文件(包括子目录下的) + EmbedXrpcRuntime/Cpp + EmbedXrpcRuntime/Cpp/FreeRTOS.Port = 服务端(单片机端)的源文件 FreeRTOS的移植

又一个简单的例子

1.编写idl文件:demo.cs

using System;
using EmbedXrpcIdlParser;

[FileName("demo.cs")]
interface Inter
{
    UInt16 GetValue();
}
[FileName("demo.cs")]
public class OptionProcess:IOptionProcess
{
    public GenerationOption Process()
    {
        GenerationOption option = new GenerationOption();
        option.OutPutFileName = "Demo";
        option.CSharpNameSpace = "Demo";
        return option;
    }
}

2.执行命令(见上述),生成客户端(电脑端)的代码和服务端代码(这里假定执行命令中,-g参数你写的是all,而不是单独的client和server),接口所示:

//客户端
class InterClientImpl
{
public:
    EmbedXrpcClientObject *RpcClientObject = nullptr;
    InterClientImpl(EmbedXrpcClientObject *rpcobj)
    {
        this->RpcClientObject = rpcobj;
    }
    Inter_GetValue_Response &GetValue();
    void Free_GetValue(Inter_GetValue_Response *response);
};


//----------服务端-----------
class Inter_GetValueService : public IService
{
public:
    uint16_t GetSid() { return Inter_GetValue_ServiceId; }
    Inter_GetValue_Response Response;
    void GetValue();
    void Invoke(SerializationManager &recManager, SerializationManager &sendManager);
};

3.服务端(单片机)这边,你需要编写代码:

EmbedXrpcServerObject ServerRpcObject(.....);//参数我这里省略了

void Inter_GetValueService::GetValue()
{
    Response.ReturnValue=188;
}

4.客户端(电脑)这边,你需要编写代码:

EmbedXrpcClientObject ClientRpcObject(....Args...);//参数我这里省略了
InterClientImpl Client(&ClientRpcObject);
auto val=Client.GetValue();//这个函数将会把数据发送到服务器(比如单片机)上
if(val.State==ResponseState_Ok)
{
    UInt16 value=val.ReturnValue;//如果不出意外的话,value的值是188
    //你的代码
}
Client.Free_GetValue(&val);//最后用完val,记得调用接口对应的释放函数,形如: Free_你的接口()

至此一个完整的RPC调用就完成了 可以看下我自己项目上实现的

auto ThreadsInfo=Dev.Request.GetThreadsInfo();
CheckResponseState(ThreadsInfo);//我自己写的一个宏,就是判断State是否是OK,如果不是OK,就返回错误的,简单辅助宏罢了
printf("...");//打印参数

auto EraseResult=Dev.Request.FlashErase(addr,size);
CheckResponseState(EraseResult);
while(...)
{
  auto WriteResult=Dev.Request.FlashWrite(addr,size,bin);
  CheckResponseState(WriteResult);
}

是不是非常方便~~

原理部分(可以不看)

序列化部分:

例如有如下结构体:

Struct sub
{
Uint8 v1;
Uint8 v2;
}
Struct ss
{
 Uint8  a;
 Uint16 b_len;
 sub[] b;
 sub c;
}

这个IDL文件通过exe后会生成序列化描述源文件,功能和高级语言的反射差不多。 假定生成代码后,有这样的语句:

Struct ss  instance;
Instance.a=1;
Instance. b_len=2;
Instance.b=malloc(....);
Instance.b[0].v1=3;
Instance.b[0].v2=4;
Instance.b[1].v1=5;
Instance.b[1].v2=6;
Instance.c.v1=7;
Instance.c.v2=8;

序列化库会根据描述源文件 会把instance序列化成字节流; 0x1,0x2,0x0(小端序),3,4,5,6,7,8;(并没有使用TLV或者TTLV编码。因为要减少内存消耗和提高效率,如果你想改成TLV,自己可以很简单改好。) 支持一维固定数组和一维动态数组(指针),不支持2维; 一般地,数组需要整数型字段(例子中的c_len)作为他的长度表示,如果没有,序列化的时候值序列化一个单位(例子中的MyStructType)长度; 总之他最终序列化的基本单位是整数u8.i8...i64 因为任何数据都是这个的集合体(不支持位域)

RPC部分:

序列化库和RPC是解耦的,你可以单独使用序列化,也可以两个一起用 假定有如下Service接口:

MyType GetValue(var a1,var a2,)

会生成一个结构体 GetValue_Request ,客户端生成GetValue同名函数,并生成如下代码:

GetValue(var a1,var a2,)
{
//填充结构体
Memcpy(GetValue_Request.a1,a1,sizeof(a1));
Memcpy....
GetValue_Request_Type.serialize(buf,request)
ClientRpcObject->Send(buf)//通过串口啥的发送出去
GetValue_Response response;
 wait(..&response)//等待串口的回复 如果接受成功,反序列化到response
Return 
}

关于使用注意:

例如例子中 结构体ss中的b这个动态数组,如果b的值是无效的。也就是说当b指向一块无效的内存的时候,这时候执行序列化代码会发生异常,因为序列化库不管这些,他只是忠实的序列化b指向的内容。所以这些部分由用户去控制。 具体的看代码吧,暂时看不懂序列化和反序列化没关系。

MIT License Copyright (c) 2020 蛋蛋 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

这个东西类似于Google的GRPC,但是应用场景是单片机。RPC远程调用极大的方便了开发,使得不必关注于协议解析,数据的序列化和反序列化等繁琐的工作。可是目前还没有在单片机上实现好用的RPC框架,于是我就谋生了做这个RPC框架的想法,所用的技术是:C#做 IDL语言+csscript+自己实现序列化和反序列化+代码生成(Razor Engine) QQ群134161401 展开 收起
C++
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化