通用的SOA 架构
一种与开发语言无关的接口定义方法,常见的有AIDL/XML等。
CommonAPI C++依靠Franca IDL来描述静态接口,根据通信协议部署参数。
XXX.fild文件:实际的接口定义将使用Franca IDL创建。
XXX.fdepl文件:根据各项IPC服务的进行文件部署定义SOA中间件-CommonAPI。
AUTOSAR 依靠ARXML语言。
CommonAPI 是由COVESA(前GENIVI)开发的一套用于开发分布式应用程序的标准SOA 中间件。
CommonAPI 架构
CommonAPI 分为Core和Binding两个部分,
开发android的朋友可以类比binder服务理解。CommonAPI 使应用程序对具体的通讯协议(如someip,或D-Bus)透明。Adaptive AutoSAR 通讯管理规范明确要求:通讯实现不绑定到特定的通讯协议,SOME/IP协议是必须支持的,但要求能替换成其它协议。因此,为使用特定IPC 例如someip的系统开发的组件可以轻松地部署到另一个使用IPC(例如D-Bus)的系统,只需要交换IPC Common API后端(someip或D-Bus),而无需重新编译应用程序代码。
AUTOSAR对SOA中间件的实现定义在AUTOSAR_SWS_CommunicationManagement.pdf
面向服务的通讯协议,协议规范由autosar组织制定 https://www.autosar.org/standards/adaptive-platform/
vsomeip是由COVESA基于SOME/IP规范开发的。
核心是两个协议,一个用于服务之间进行数据交互称作SOME/IP协议,一个用于服务发现称作SOME/IP-SD协议。
车载以太网常用的SOME/IP协议实际是应用层协议,它是基于TCP或UDP的。
SOME/IP协议的 Request/Response Communication 机制(相当于RPC,Event相当于消息通讯。在实现上,我们也可以利用RPC机制去实现消息通讯,也可以利用消息通讯去实现RPC调用。“中间件”的最小核心关注的是数据通讯。
SOME/IP在如下OSI分层中的位置。
典型车载以太网有100Mbps,1000Mbps。
车载以太网包含一系列协议簇,见上图,在IT以太网的基础扩充。
物理层使用100Base-T1/1000Base-T1。数据链路层采用IEEE 802.1Q标准并强化VLAN的使用。在应用层增加UDS/DoIP诊断协议、SOME/IP协议、AVB协议。
// 1: 获取runtime
std::shared_ptr runtime = CommonAPI::Runtime::get();
// 2: 绑定服务,获取客户端代理
std::string domain = "local";
std::string instance = "commonapi.examples.Vehicle";
std::string connectionid = "client-sample";
std::shared_ptr> myProxy = runtime->buildProxy(domain, instance, "client-sample");
// 等待服务绑定成功
while (!myProxy->isAvailable()) {
std::this_thread::sleep_for(std::chrono::microseconds(10));
}
// 3.接口调用
while (true) {
myProxy->sayHello(name, callStatus, returnMessage, &info);
}
创建SOA Runtime单例,进行init初试化
//CommonAPI/Runtime.cpp
std::shared_ptr Runtime::get() {
...
if(!theRuntimePtr__) {
theRuntimePtr__ = new std::shared_ptr();
}
if (theRuntimePtr__) {
if (!*theRuntimePtr__) {
*theRuntimePtr__ = std::make_shared();
// init初试化
(*theRuntimePtr__)->init();
}
return *theRuntimePtr__;
}
return nullptr;
}
init里边执行
defaultConfig_ = /etc/commonapi.ini
isConfigured_ 构造时传的是false,
//CommonAPI/Runtime.cpp
void Runtime::init() {
...
if (!isConfigured_) {
// Determine default configuration file
const char *config = getenv("COMMONAPI_CONFIG");
if (config) {
defaultConfig_ = config;
} else {
defaultConfig_ = COMMONAPI_DEFAULT_CONFIG_FOLDER;
defaultConfig_ += "/";
defaultConfig_ += COMMONAPI_DEFAULT_CONFIG_FILE;
}
// TODO: evaluate return parameter and decide what to do
(void)readConfiguration();
// Determine default ipc & shared library folder
const char *binding = getenv("COMMONAPI_DEFAULT_BINDING");
if (binding)
defaultBinding_ = binding;
const char *folder = getenv("COMMONAPI_DEFAULT_FOLDER");
if (folder)
defaultFolder_ = folder;
isConfigured_ = true;
}
}
先从工作目录读取,读取不到从默认/etc目录读取配置文件
defaultBinding_ = someip
//CommonAPI/Runtime.cpp
bool Runtime::readConfiguration() {
#define MAX_PATH_LEN 255
std::string config;
bool tryLoadConfig(true);
char currentDirectory[MAX_PATH_LEN];
#ifdef _WIN32
if (GetCurrentDirectory(MAX_PATH_LEN, currentDirectory)) {
#else
if (getcwd(currentDirectory, MAX_PATH_LEN)) {
#endif
usedConfig_ = currentDirectory;
usedConfig_ += "/";
usedConfig_ += COMMONAPI_DEFAULT_CONFIG_FILE;
struct stat s;
if (stat(usedConfig_.c_str(), &s) != 0) {
usedConfig_ = defaultConfig_;
if (stat(usedConfig_.c_str(), &s) != 0) {
tryLoadConfig = false;
}
}
}
IniFileReader reader;
if (tryLoadConfig && !reader.load(usedConfig_))
return false;
std::string itsConsole("true");
std::string itsFile;
std::string itsDlt("false");
std::string itsLevel("info");
std::shared_ptr section
= reader.getSection("logging");
if (section) {
itsConsole = section->getValue("console");
itsFile = section->getValue("file");
itsDlt = section->getValue("dlt");
itsLevel = section->getValue("level");
}
Logger::init((itsConsole == "true"),
itsFile,
(itsDlt == "true"),
itsLevel);
section = reader.getSection("default");
if (section) {
std::string binding = section->getValue("binding");
if ("" != binding) {
defaultBinding_ = binding;
}
std::string folder = section->getValue("folder");
if ("" != folder) {
defaultFolder_ = folder;
}
std::string callTimeout = section->getValue("callTimeout");
if ("" != callTimeout) {
defaultCallTimeout_ = std::stoi(callTimeout);
}
}
section = reader.getSection("proxy");
if (section) {
for (auto m : section->getMappings()) {
COMMONAPI_DEBUG("Adding proxy mapping: ", m.first, " --> ", m.second);
libraries_[m.first][true] = m.second;
}
}
section = reader.getSection("stub");
if (section) {
for (auto m : section->getMappings()) {
COMMONAPI_DEBUG("Adding stub mapping: ", m.first, " --> ", m.second);
libraries_[m.first][false] = m.second;
}
}
return true;
}
配置文件格式:
[default]
binding=someip
[logging]
console = true
file = ./mylog.log
dlt = true
level = verbose
传入domain , instance,_connectionId
CommonAPI/Runtime.hpp
buildProxy(const std::string &_domain,
const std::string &_instance,
const ConnectionId_t &_connectionId = DEFAULT_CONNECTION_ID) {
std::shared_ptr proxy
= createProxy(_domain,
ProxyClass_::getInterface(),
_instance,
_connectionId);
if (proxy) {
return std::make_shared>(proxy);
}
return nullptr;
}
ProxyClass_::getInterface(),
对应如下代码,根据FIDL文件自动生成的代码。
// XXX.h
const char* Vehicle::getInterface() {
return ("commonapi.examples.Vehicle:v1_2");
}
Runtime::createProxy
//CommonAPI/Runtime.cpp
std::shared_ptr
Runtime::createProxy(
const std::string &_domain, const std::string &_interface, const std::string &_instance,
const ConnectionId_t &_connectionId) {
if (!isInitialized_) {
initFactories();
}
// Check whether we already know how to create such proxies...
std::shared_ptr proxy = createProxyHelper(_domain, _interface, _instance, _connectionId, false);
if (!proxy) {
// ...it seems do not, lets try to load a library that does...
std::lock_guard itsGuard(loadMutex_);
std::string library = getLibrary(_domain, _interface, _instance, true);
if (loadLibrary(library) || defaultFactory_) {
proxy = createProxyHelper(_domain, _interface, _instance, _connectionId, true);
}
}
return proxy;
}
initFactories
调用Factory的init函数,但是Factory在哪赋值的呢,见如下分析
//CommonAPI/Runtime.cpp
Runtime::initFactories() {
std::lock_guard itsLock(factoriesMutex_);
if (!isInitialized_) {
COMMONAPI_INFO("Loading configuration file \'", usedConfig_, "\'");
COMMONAPI_INFO("Using default binding \'", defaultBinding_, "\'");
COMMONAPI_INFO("Using default shared library folder \'", defaultFolder_, "\'");
if (defaultFactory_)
defaultFactory_->init();
for (auto f : factories_)
f.second->init();
isInitialized_ = true;
}
}
Factory注册是在CommonAPI-SomeIP的Factory类中,这里是一个典型的抽象工厂类,针对someip实现的一个commonapi中间件,你也可以针对dbus或其他通讯协议实现对应的CommonAPI中间件。
//CommonAPI/SomeIP/Factory.cpp
INITIALIZER(FactoryInit) {
Factory::runtime_ = Runtime::get();
Factory::runtime_.lock()->registerFactory("someip", Factory::get());
}
defaultBinding_也是someip,所以这里会给defaultFactory_ 赋值。
//CommonAPI/Runtime.cpp
bool Runtime::registerFactory(const std::string &_binding, std::shared_ptr _factory) {
bool isRegistered(false);
#ifndef _WIN32
std::lock_guard itsLock(factoriesMutex_);
#endif
if (_binding == defaultBinding_) {
defaultFactory_ = _factory;
isRegistered = true;
} else {
auto foundFactory = factories_.find(_binding);
if (foundFactory == factories_.end()) {
factories_[_binding] = _factory;
isRegistered = true;
}
}
if (isRegistered && isInitialized_)
_factory->init();
return isRegistered;
}
继续回到factory->init(),这里啥也没干。。。
//CommonAPI/SomeIP/Factory.cpp
void Factory::init() {
if (!isInitialized_) {
for (auto i : initializers_) i();
initializers_.clear(); // Not needed anymore
isInitialized_ = true;
}
}
Runtime::createProxyHelper
factories_是空的,所以最终是调用defaultFactory_也是空的,实际是加载so
//CommonAPI/Runtime.cpp
std::shared_ptr
Runtime::createProxyHelper(const std::string &_domain, const std::string &_interface, const std::string &_instance,
const std::string &_connectionId, bool _useDefault) {
std::lock_guard itsLock(factoriesMutex_);
for (auto factory : factories_) {
std::shared_ptr proxy
= factory.second->createProxy(_domain, _interface, _instance, _connectionId);
if (proxy)
return proxy;
}
return (_useDefault && defaultFactory_ ?
defaultFactory_->createProxy(_domain, _interface, _instance, _connectionId)
: nullptr);
}
CPI-SIP-Factory::createProxy
又回到capi-someip的Factory类
//CommonAPI/SomeIP/Factory.cpp
std::shared_ptr
Factory::createProxy(
const std::string &_domain,
const std::string &_interface, const std::string &_instance,
const ConnectionId_t &_connectionId) {
COMMONAPI_VERBOSE("Creating proxy for \"", _domain, ":", _interface, ":",
_instance, "\"");
auto proxyCreateFunctionsIterator
= proxyCreateFunctions_.lower_bound(_interface);
if (proxyCreateFunctionsIterator
!= proxyCreateFunctions_.end()) {
std::string itsInterface(_interface);
if (proxyCreateFunctionsIterator->first != _interface) {
std::string itsInterfaceMajor(_interface.substr(0, _interface.find('_')));
if (proxyCreateFunctionsIterator->first.find(itsInterfaceMajor) != 0)
return nullptr;
itsInterface = proxyCreateFunctionsIterator->first;
}
CommonAPI::Address address(_domain, itsInterface, _instance);
Address someipAddress;
// CommonAPI address 转换为someipAddress
if (AddressTranslator::get()->translate(address, someipAddress)) {
std::shared_ptr itsConnection
= getConnection(_connectionId);
if (itsConnection) {
std::shared_ptr proxy
= proxyCreateFunctionsIterator->second(
someipAddress, itsConnection);
if (proxy && proxy->init())
return proxy;
}
}
}
COMMONAPI_ERROR("Creating proxy for \"", _domain, ":", _interface, ":",
_instance, "\" failed!");
return nullptr;
}
CAPI-SIP::Factory::getConnection
//CommonAPI/SomeIP/Factory.cpp
std::shared_ptr
Factory::getConnection(const ConnectionId_t &_connectionId) {
std::unique_lock itsLock(connectionMutex_);
auto itsConnectionIterator = connections_.find(_connectionId);
if (itsConnectionIterator != connections_.end()) {
incrementConnection(itsConnectionIterator->second);
return itsConnectionIterator->second;
}
// No connection found, lets create and initialize one
std::shared_ptr itsConnection
= std::make_shared(_connectionId);
if (itsConnection) {
if (!itsConnection->connect(true)) {
COMMONAPI_ERROR("Failed to create connection ", _connectionId);
itsConnection.reset();
} else {
connections_.insert({ _connectionId, itsConnection } );
}
}
if(itsConnection)
incrementConnection(itsConnection);
return itsConnection;
}
构造Connection
_name 为 _connectionId
//CommonAPI/SomeIP/Connection.cpp
Connection::Connection(const std::string &_name)
: dispatchSource_(NULL),
watch_(NULL),
connectionStatus_(state_type_e::ST_DEREGISTERED),
application_(vsomeip::runtime::get()->create_application(_name)),
//vsomeip/implementation/runtime/src/runtime_impl.cpp
std::shared_ptr runtime_impl::create_application(
const std::string &_name) {
static std::uint32_t postfix_id = 0;
std::lock_guard its_lock(applications_mutex_);
std::string its_name_ = _name;
auto found_application = applications_.find(_name);
if( found_application != applications_.end()) {
its_name_ += "_" + std::to_string(postfix_id++);
}
std::shared_ptr application = std::make_shared(its_name_);
applications_[its_name_] = application;
return application;
}
连接服务
// CommonAPI/SomeIP/Connection.cpp
bool Connection::connect(bool) {
if (!application_->init())
return false;
std::function connectionHandler = std::bind(&Connection::onConnectionEvent,
shared_from_this(),
std::placeholders::_1);
application_->register_state_handler(connectionHandler);
asyncAnswersCleanupThread_ = std::make_shared(&Connection::cleanup, this);
dispatchThread_ = std::make_shared(&Connection::dispatch, this);
return true;
}
// vsomeip/implementation/runtime/src/application_impl.cpp
bool application_impl::init() {
//加载config
}