网站工作室设计,教人做网站的视频,羽毛球赛事名称,酒店网站解决方案一. 概述
这篇文章主要是想整理并且分析CommonAPI代码生成工具根据fidl和fdepl配置文件生成出来的代码的结构和作用。
二. fidl
用户根据业务需求在fidl文件中定义业务服务接口的结构以及自定义数据类型#xff0c;然后使用core生成工具传入fidl文件生成该fidl的核心…一. 概述
这篇文章主要是想整理并且分析CommonAPI代码生成工具根据fidl和fdepl配置文件生成出来的代码的结构和作用。
二. fidl
用户根据业务需求在fidl文件中定义业务服务接口的结构以及自定义数据类型然后使用core生成工具传入fidl文件生成该fidl的核心层Proxy和Stub以及配套的相关代码。
例如官方范例上的HelloWorldhttps://github.com/COVESA/capicxx-core-tools/blob/master/CommonAPI-Examples/E01HelloWorld/fidl/E01HelloWorld.fidl使用生成工具commonapi-core-generator-linux-x86_64输入该fidl文件可以生成HelloWorld.hppHelloWorldProxy.hppHelloWorldProxyBase.hppHelloWorldStub.hppHelloWorldStubDefault.hpp这5个代码文件。
2.1 interface
一个fidl文件对应一个CommonAPI的interface对于C来说要定义一个interface当然是使用一个抽象类来定义了这个抽象类就是HelloWorld.hpp文件中的HelloWorld类了。
namespace v1 {
namespace commonapi {class HelloWorld {
public:virtual ~HelloWorld() { }static inline const char* getInterface(); // HelloWorld业务的CommonAPI接口名称static inline CommonAPI::Version getInterfaceVersion(); // HelloWorld业务的CommonAPI接口版本
};const char* HelloWorld::getInterface() {return (commonapi.HelloWorld:v1_0);
}CommonAPI::Version HelloWorld::getInterfaceVersion() {return CommonAPI::Version(1, 0);
}} // namespace commonapi
} // namespace v1 当看到core工具生成的HelloWorld接口类感觉有点奇怪这个HelloWorld接口类只定义并且实现了两个关于接口信息的函数接口名称和接口版本信息而fidl中关于HelloWorld接口中的业务函数sayHello在生成的HelloWorld接口类中确没有
// HelloWorld.fidl
package commonapiinterface HelloWorld {version {major 1 minor 0}method sayHello { // 该函数没有生成在HelloWorld接口类中in {String name}out {String message}}
} 对于为什么生成的HelloWorld接口类中没有定义sayHello函数的原因个人阅读完生成的代码后理解是由于HelloWorld.fidl生成的核心层Proxy以及Stub代码在sayHello函数上的定义是不兼容的而HelloWorld接口类又是核心层HelloWorldProxy和HelloWorldStub的父类因此无法将sayHello函数的申明放入HelloWorld接口类中。
HelloWorld接口类中的getInterface和getInterfaceVersion函数是为了给CommonAPI核心层代码库创建核心层Proxy/Stub的时候使用的代码如下:
// 代码文件/usr/local/include/CommonAPI-3.2/CommonAPI/Runtime.hpp
templatetemplatetypename ... class ProxyClass_, typename ... AttributeExtensions_COMMONAPI_EXPORT std::shared_ptrProxyClass_AttributeExtensions_...buildProxy(const std::string _domain,const std::string _instance,const ConnectionId_t _connectionId DEFAULT_CONNECTION_ID) {std::shared_ptrProxy proxy createProxy(_domain,//这里的ProxyClass就是HelloWorldProxy类调用HelloWorldProxy的父类HelloWorld中的getInterfaceProxyClass_AttributeExtensions_...::getInterface(), _instance,_connectionId);if (proxy) {return std::make_sharedProxyClass_AttributeExtensions_...(proxy);}return nullptr;}
2.2 Proxy
在核心层生成了HelloWorldProxy.hpp和HelloWorldProxyBase.hpp两个代码文件里面分别定义了HelloWorldProxy和HelloWorldProxyBase两个类。
其中HelloWorldProxyBase是HelloWorldProxy的一个父类其中包含了fidl中sayHello函数的相关定义(包括了同步调用和异步调用两种)
class HelloWorldProxyBase: virtual public CommonAPI::Proxy {
public:typedef std::functionvoid(const CommonAPI::CallStatus, const std::string) SayHelloAsyncCallback; // 异步调用的回调通知接口virtual void sayHello(std::string _name, CommonAPI::CallStatus _internalCallStatus, std::string _message, const CommonAPI::CallInfo *_info nullptr) 0; // 同步调用接口virtual std::futureCommonAPI::CallStatus sayHelloAsync(const std::string _name, SayHelloAsyncCallback _callback nullptr, const CommonAPI::CallInfo *_info nullptr) 0; // 异步调用接口virtual std::futurevoid getCompletionFuture() 0;
}; HelloWorldProxy类中实现了HelloWorldProxyBase父类中定义的sayHello相关接口:
template typename ... _AttributeExtensions
void HelloWorldProxy_AttributeExtensions...::sayHello(std::string _name, CommonAPI::CallStatus _internalCallStatus, std::string _message, const CommonAPI::CallInfo *_info) {delegate_-sayHello(_name, _internalCallStatus, _message, _info);
} 可以看到HelloWorldProxy内部实现fidl中定义的接口时还依赖了一个delegate_对象这个delegatge_对象是HelloWorldProxyBase类型的共享指针
template typename ... _AttributeExtensions
class HelloWorldProxy: virtual public HelloWorld,virtual public HelloWorldProxyBase,virtual public _AttributeExtensions... {public:...private:std::shared_ptr HelloWorldProxyBase delegate_;
}; 这里就有点不对了前面说过HelloWorldProxy的一个父类就是HelloWorldProxyBase难道HelloWorldProxy对象内部的这个delegate_共享指针是指向自己的当然不是在src-gen目录下查找还有没有其他类是HelloWorldProxy结果看到HelloWorldProxyBase类也是HelloWorldSomeIPProxy类的父类HelloWorldSomeIPProxy是通过commonapi-someip-generator-linux-x86_64工具生成的绑定层的Proxy类。
也就是说核心层的Proxy是依赖绑定层的Proxy的绑定层的内部会产生依赖所绑定中间件的代码来实现fidl中定义的接口函数。
2.3 Stub
在核心层生成了HelloWorldStub.hppHelloWorldStubDefault.hpp这两个代码文件其中包含HelloWorldStubAdapterHelloWorldStubRemoteEvent HelloWorldStub以及HelloWorldStubDefault这四个类这四个类的关系如下 首先是HelloWorldStubAdapter类其继承于CommonAPI::StubAdapter实现了CommonAPI地址提供接口getAddress和HelloWorld实现了接口信息提供方法getInterface和getInterfaceVersion。
所谓CommonAPI地址包含三个成员domain interface instance这三个成员都是字符串:
namespace CommonAPI {
class Address {
public:COMMONAPI_EXPORT Address();...private:std::string domain_; // 所在域std::string interface_; // 接口名称std::string instance_; // 实例名称friend COMMONAPI_EXPORT std::ostream operator(std::ostream _out, const Address _address);
};
} CommonAPI::StubAdapter中CommonAPI地址的来源是绑定层的StubAdapterCommonAPI::SomeIP::StubAdapter在初始化的时候赋值的
// Stub.hpp
namespace CommonAPI {class StubAdapter {public:virtual ~StubAdapter() {}inline const Address getAddress() const { return address_; }protected:Address address_; // 在绑定层的StubAdapter中赋值};
}// StubAdapter.cpp
namespace CommonAPI {
namespace SomeIP {
class StubAdapter: virtual public CommonAPI::StubAdapter, public InterfaceHandler { // 继承自核心层的StubAdapter
...
void
StubAdapter::init(std::shared_ptrStubAdapter instance) {(void) instance;// AddressTranslator保存了绑定层地址和核心层地址的映射关系// 这个映射关系在绑定层StubAdapter的initializer函数中插入的AddressTranslator::get()-translate(someipAddress_, address_); // address_是CommonAPI::StubAdapter中的成员变量
}
...
}
} 其次是HelloWorldStubRemoteEvent类这个类目前基本是空的因为这是个和属性相关的类如果你的fidl中没有定义属性成员那么生成的StubRemoteEvent类可能就是空的对照官网上https://github.com/COVESA/capicxx-core-tools/tree/master/CommonAPI-Examples/E02Attributes这个带属性的范例生成的代码可以看到有属性的fidl生成的StubRemoteEvent类是定义了一些属性相关的接口的例如
class E02AttributesStubRemoteEvent
{
public:virtual ~E02AttributesStubRemoteEvent() { }/// Verification callback for remote set requests on the attribute xvirtual bool onRemoteSetXAttribute(const std::shared_ptrCommonAPI::ClientId _client, int32_t _value) 0;/// Action callback for remote set requests on the attribute xvirtual void onRemoteXAttributeChanged() 0;/// Verification callback for remote set requests on the attribute a1virtual bool onRemoteSetA1Attribute(const std::shared_ptrCommonAPI::ClientId _client, ::v1::commonapi::examples::CommonTypes::a1Struct _value) 0;/// Action callback for remote set requests on the attribute a1virtual void onRemoteA1AttributeChanged() 0;
}; 也就是说生成的这个StubRemoteEent类是个关于事件通知的接口类是需要用户自己来实现内部接口来接收属性的变化通知。
接者是HelloWorldStub类这也是个接口类继承自CommonAPI::StubCommonAPI::Stub最重要的工作是和CommonAPI::StubHelper打交道
namespace CommonAPI {
templatetypename StubAdapter_, typename StubRemoteEventHandler_
class Stub: public virtual StubBase {
public:// 初始化StubAdaptervirtual StubRemoteEventHandler_* initStubAdapter(const std::shared_ptrStubAdapter_ _stubAdapter) 0;// 返回StubAdapterinline const std::shared_ptrStubAdapter_ getStubAdapter() const { return stubAdapter_.lock(); }protected:std::weak_ptrStubAdapter_ stubAdapter_; 因此HelloWorldStub类包含了初始化和返回StubAdapter的接口此外还增加了fidl中sayHello接口的申明也就是说HelloWorldStub接口类的实现类至少要实现initStubAdaptergetStubAdapter和sayHello三个接口。
最后就是HelloWorldStubDefault类它就是实现上面HelloWorldStub接口类中三个接口的实现类代码如下
class COMMONAPI_EXPORT_CLASS_EXPLICIT HelloWorldStubDefault: public virtual HelloWorldStub {
public:COMMONAPI_EXPORT HelloWorldStubDefault(): remoteEventHandler_(this),interfaceVersion_(HelloWorld::getInterfaceVersion()) {}COMMONAPI_EXPORT const CommonAPI::Version getInterfaceVersion(std::shared_ptrCommonAPI::ClientId _client) {(void)_client;return interfaceVersion_; // 返回接口版本}COMMONAPI_EXPORT HelloWorldStubRemoteEvent* initStubAdapter(const std::shared_ptr HelloWorldStubAdapter _adapter) {CommonAPI::StubHelloWorldStubAdapter, HelloWorldStubRemoteEvent::stubAdapter_ _adapter;return remoteEventHandler_;}COMMONAPI_EXPORT virtual void sayHello(const std::shared_ptrCommonAPI::ClientId _client, std::string _name, sayHelloReply_t _reply) {(void)_client;(void)_name;std::string message ;_reply(message);}... HelloWorldStubDefault类是一个默认的生成的Stub接口实现类里面的函数实现大多数是无用的用户需要在HelloWorldStubDefault这个生成的默认Stub实现类上再做一次实现继承并且实现例如代码实例中的HelloWorldStubImpl类那样。
三. fdepl
fdepl文件生成绑定层的代码会生成HelloWorldSomeIPProxyHelloWorldSomeIPStubAdapterInternalHelloWorldSomeIPStubAdapter这几个绑定层的类。
3.1 Proxy
HelloWorldSomeIPProxy类是绑定层生成的Proxy类其构造时需要传入绑定层地址(CommonAPI::SomeIP::Address)CommonAPI::SomeIP::Address包含SomeIP服务实例的信息
namespace CommonAPI {
namespace SomeIP {class COMMONAPI_EXPORT Address {...
private:service_id_t service_; // 服务IDinstance_id_t instance_; // 实例IDmajor_version_t major_version_; // Max版本号minor_version_t minor_version_; // Min版本号
}
}
} 先来看下创建HelloWorldSomeIPProxy的过程首先用户需要在自己的应用程序中创建核心层的Proxy(HelloWorldProxy)
// HelloWorldClient.cpp
int main() {...// CommonAPI::Address// {// domainid local// interface HelloWorld::getInterface commonapi.HelloWorld:v1_0// instance test// }std::shared_ptrHelloWorldProxy myProxy runtime-buildProxyHelloWorldProxy(local, test);...
} 可以看到创建核心层HelloWorldProxy的时候对应的CommonAPI::Address地址已经提供出来了然后runtime在创建的时候会在调用核心层的工厂类(CommonAPI::SomeIP::Factory)的createProxy创建绑定层的HelloWorldSomeIPProxy对象这个时候就需要从AddressTranslator中获取绑定层地址SomeipIP的服务和实例信息
// Factory.cppstd::shared_ptrCommonAPI::Proxy
Factory::createProxy(const std::string _domain,const std::string _interface, const std::string _instance,const ConnectionId_t _connectionId) {auto proxyCreateFunctionsIterator proxyCreateFunctions_.lower_bound(_interface);// 查找是否为该interface注册过SomeIP绑定层的Proxy的创建函数if (proxyCreateFunctionsIterator! proxyCreateFunctions_.end()) { // 找到该CommonAPI interface注册的SomeIP绑定层Proxy创建函数...CommonAPI::Address address(_domain, itsInterface, _instance);Address someipAddress;// 在AddressTranslator中查找CommonAPI地址对应的SomeIP地址if (AddressTranslator::get()-translate(address, someipAddress)) {std::shared_ptrConnection itsConnection getConnection(_connectionId);if (itsConnection) {// 使用注册的Proxy创建函数createHelloWorldSomeIPProxy创建HelloWorldSomeIPProxy// 传入的CommonAPI::SomeIP::Address为{0x1234, 0x1, 1, 0}std::shared_ptrProxy proxy proxyCreateFunctionsIterator-second(someipAddress, itsConnection);if (proxy proxy-init())return proxy;}}}COMMONAPI_ERROR(Creating proxy for \, _domain, :, _interface, :,_instance, \ failed!);return nullptr;
}
AddressTranslator中CommonAPI地址和SomeIP绑定层地址的对应关系在生成的绑定层HelloWorldSomeIPProxy.cpp中插入的
// HelloWorldSomeIPProxy.cpp
void initializeHelloWorldSomeIPProxy() {// 插入地址对应关系CommonAPI::SomeIP::AddressTranslator::get()-insert(local:commonapi.HelloWorld:v1_0:test, // CommonAPI Address0x1234, 0x1, 1, 0); // CommonAPI::SomeIP Address// 注册绑定层SomeIP创建函数CommonAPI::SomeIP::Factory::get()-registerProxyCreateMethod(commonapi.HelloWorld:v1_0, // CommonAPI interfacecreateHelloWorldSomeIPProxy); // SomeIP Proxy create function
}std::shared_ptrCommonAPI::SomeIP::Proxy createHelloWorldSomeIPProxy(const CommonAPI::SomeIP::Address _address,const std::shared_ptrCommonAPI::SomeIP::ProxyConnection _connection) {return std::make_shared HelloWorldSomeIPProxy(_address, _connection);
} 然后生成的绑定层HelloWorldSomeIPProxy也是需要实现fidl中定义的sayHello接口的其实现主要是依赖Deployment类来包装输入的参数然后通过ProxyHelper类来完成参数的序列化和中间件通信接口的调用ProxyHelper::callMethod
void HelloWorldSomeIPProxy::sayHello(std::string _name, CommonAPI::CallStatus _internalCallStatus, std::string _message, const CommonAPI::CallInfo *_info) {CommonAPI::Deployable std::string, CommonAPI::SomeIP::StringDeployment deploy_name(_name, static_cast CommonAPI::SomeIP::StringDeployment* (nullptr)); // 输入参数CommonAPI::Deployable std::string, CommonAPI::SomeIP::StringDeployment deploy_message(static_cast CommonAPI::SomeIP::StringDeployment* (nullptr)); // 返回参数// 依赖ProxyHelper类发起someip的Method请求REQUEST-RESPONSECommonAPI::SomeIP::ProxyHelper...::callMethodWithReply( // ProxyHelper类是个静态类内部都是静态方法不保存成员*this, // 主要是提供HelloWorldSomeIPProxy内部的Connection指针CommonAPI::SomeIP::method_id_t(0x7b),false,false,(_info ? _info : CommonAPI::SomeIP::defaultCallInfo),deploy_name,_internalCallStatus,deploy_message);_message deploy_message.getValue();
} 而ProxyHelper的callMethod函数则依赖Connetion类来完成中间件接口的调用。
template typename Proxy_ Proxy
static void callMethod(...) {if (_proxy.isAvailable()) { // 对端service为可用状态// 首先序列化参数OutputStream outputStream(_methodCall, _isLittleEndian);const bool success SerializableArgumentsInArgs_...::serialize(outputStream, _inArgs...);...// 通过Connection指针调用到vsomeip中间件接口bool success _proxy.getConnection()-sendMessage(_methodCall); ...} else {...}
}3.2 Stub
HelloWorldSomeIPStubAdapterInternal是HelloWorldSomeIPStubAdapter的父类此外HelloWorldSomeIPStubAdapterInternal类也是绑定层对于核心层HelloWorldStubAdapter接口类的实现。
总体来说StubAdapter类是在Stub和中间件之间做适配功能的类并且实现了核心层的StubAdapter接口类中定义的接口。
先看下HelloWorldSomeIPStubAdapterInternal类HelloWorldSomeIPStubAdapterInternal继承自核心层生成的HelloWorldStubAdapter接口和CommonAPI::SomeIP::StubAdapterHelper类。
HelloWorldSomeIPStubAdapterInternal类有获取内部属性的成员其中包括Version信息的属性的getHelloWorldInterfaceVersionStubDispatcher成员如果fidl中定义了业务的属性则还会包含该业务属性的StubDispatcher成员例如https://github.com/COVESA/capicxx-core-tools/tree/master/CommonAPI-Examples/E02Attributes 中fdepl生成的E02AttributesSomeIPStubAdapter.hpp文件中E02AttributesSomeIPStubAdapterInternal类就有getXAttributeStubDispatchergetA1AttributeStubDispatcher和getE02AttributesInterfaceVersionStubDispatcher其中getXAttributeStubDispatcher对应fidl中定义的属性xgetA1AttributeStubDispatcher对应fidl中定义的属性a1。
SomeIPStubAdapterInternal类内部的StubDispatcher成员都是GetAttributeStubDispatcher类型的实现了StubDispatcher接口查看GetAttributeStubDispatcher类的实现可以看到内部成员函数dispatchMessage这个函数就是通过Connection类调用SomeIP中间件将属性值通过消息的方式发送给客户端
template typename StubClass_, typename AttributeType_, typename AttributeDepl_ EmptyDeployment
class GetAttributeStubDispatcher: public StubDispatcherStubClass_ {
public:...bool dispatchMessage(const Message message, const std::shared_ptrStubClass_ stub,RemoteEventHandlerType* _remoteEventHandler,std::shared_ptrProxyConnection _connection) {...return sendAttributeValueReply(message, stub, _connection);}protected: inline bool sendAttributeValueReply(const Message message, const std::shared_ptrStubClass_ stub,std::shared_ptrProxyConnection _connection) {...return _connection-sendMessage(reply);}...} 当服务端进程收到客户端发送的对属性访问的SOMEIP method消息触发该接口的调用调用栈如下
Connection::handleStubReceive // 从中间件收到client发送的消息// 处理消息根据消息中的serviceID, instanceID找到对应的CommonAPI::SomeIP::StubAdapter// 例如E02AttributesSomeIPStubAdapterInternalHelloWorldSomeIPStubAdapterInternal StubManager::handleMessage StubAdapterHelper::onInterfaceMessage // StubAdapterHelper也是E02AttributesSomeIPStubAdapterInternal的父类StubAdapterHelper::findDispatcherAndHandleStubDispatcher::dispatchMessage // 内部调用Stub对应的get属性值的方法获取当前属性值 当服务端进程将属性的值通过SOMEIP消息回复给客户端的时候首先是需要知道当前服务instance中该属性的值对不对fidl文件生成的核心层Stub中提供这个属性的值的获取方法
// src-gen/v1/commonapi/examples/E02AttributesStubDefault.hpp
COMMONAPI_EXPORT virtual const int32_t getXAttribute() {return xAttributeValue_;
} 也就是说如果有Stub对象就可以调用其getXAttribute方法获取其x属性的当前值然后在E02AttributesSomeIPStubAdapterInternal的构造函数中可以看到HelloWorldSomeIPStubAdapterInternal构造函数中创建x属性对应的getXAttributeStubDispatcher对象时提供getXAttribute方法
E02AttributesSomeIPStubAdapterInternal(...getXAttributeStubDispatcher(::v1::commonapi::examples::E02AttributesStub::lockXAttribute,::v1::commonapi::examples::E02AttributesStub::getXAttribute, // 注册到内部getStubFunctor_成员中false,_stub-hasElement(0)),22...
} getXAttributeStubDispatcher是GetAttributeStubDispatcher类型的其dispatchMessage方法中可以看到对get属性值方法的调用
class GetAttributeStubDispatcher: public StubDispatcherStubClass_ {
public:...bool dispatchMessage(const Message message, const std::shared_ptrStubClass_ stub,...return sendAttributeValueReply(message, stub, _connection);}
protected:inline bool sendAttributeValueReply(const Message message, const std::shared_ptrStubClass_ stub,std::shared_ptrProxyConnection _connection) {Message reply message.createResponseMessage();OutputStream outputStream(reply, isLittleEndian_);...// 获取属性值auto deployable CommonAPI::DeployableAttributeType_, AttributeDepl_((stub.get()-*getStubFunctor_)(clientId), depl_);outputStream deployable; // 写入消息outputStream.flush();return _connection-sendMessage(reply); // 发送属性值get-method消息的回复消息}
}HelloWorldSomeIPStubAdapter继承了HelloWorldSomeIPStubAdapterInternal也就具备了通过绑定层调用SomeIP中间件进行通信的功能在此基础上主要增加了一个功能就是在构造函数中增加了SomeIP地址和Connection对象的传入有了这两个对象HelloWorldSomeIPStubAdapterInternal的SomeIP通信功能才能真正工作起来。
public:HelloWorldSomeIPStubAdapter(const CommonAPI::SomeIP::Address _address,const std::shared_ptrCommonAPI::SomeIP::ProxyConnection _connection,const std::shared_ptrCommonAPI::StubBase _stub): CommonAPI::SomeIP::StubAdapter(_address, _connection),HelloWorldSomeIPStubAdapterInternal_Stub, _Stubs...(_address, _connection, _stub) {} 那么这个构造函数什么时候调用的呢是CommonAPI::SomeIP::Factory调用的之前在核心层说过生成的绑定层Stub代码会将HelloWorldSomeIPStubAdapter的创建函数和对应的CommonAPI::Address注册给Factory
void initializeHelloWorldSomeIPStubAdapter() {CommonAPI::SomeIP::AddressTranslator::get()-insert(local:commonapi.HelloWorld:v1_0:test,0x1234, 0x1, 1, 0);CommonAPI::SomeIP::Factory::get()-registerStubAdapterCreateMethod( // 注册HelloWorldSomeIPStubAdapter的创建函数commonapi.HelloWorld:v1_0,createHelloWorldSomeIPStubAdapter);
} 当我们在服务端代码中注册我们的HelloWorldStubImpl时通过核心层调用到绑定层的SomeIP::Factory进行HelloWorldSomeIPStubAdapter对象的创建
int main() {std::shared_ptrCommonAPI::Runtime runtime CommonAPI::Runtime::get();std::shared_ptrHelloWorldStubImpl myService std::make_sharedHelloWorldStubImpl();runtime-registerService(local, test, myService); // 这一步会调用到SomeIP::Factory创建HelloWorldSomeIPStubAdapter...
}