车载以太网SOA及接口调用分析

一、车载SOA架构简介

通用的SOA 架构

接口描述语言

一种与开发语言无关的接口定义方法,常见的有AIDL/XML等。

CommonAPI C++依靠Franca IDL来描述静态接口,根据通信协议部署参数。

XXX.fild文件:实际的接口定义将使用Franca IDL创建。

XXX.fdepl文件:根据各项IPC服务的进行文件部署定义SOA中间件-CommonAPI。

AUTOSAR 依靠ARXML语言。

SOA中间件

CommonAPI 是由COVESA(前GENIVI)开发的一套用于开发分布式应用程序的标准SOA 中间件。

CommonAPI 架构

CommonAPI 分为Core和Binding两个部分,

  • 其中Core部分负责和应用程序交互。
  • 其中Binding即IPC的具体实现方式,负责和系统底层的IPC服务交互。目前GENIVI支持的Binding分为两种,分别是SOME/IP和DBus。

开发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

IPC之SOME/IP( Scalable service-Oriented MiddlewarE over IP)协议

面向服务的通讯协议,协议规范由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协议。

二、客户端使用CommonAPI进行SOA开发的示例代码:

// 1: 获取runtime 
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();

// 2: 绑定服务,获取客户端代理
std::string domain = "local";
std::string instance = "commonapi.examples.Vehicle";
std::string connectionid = "client-sample";
std::shared_ptr<VehicleProxy<>> myProxy = runtime->buildProxy<VehicleProxy>(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);
}   

三、初始化代码流程分析

1.Runtime::get()

创建SOA Runtime单例,进行init初试化

//CommonAPI/Runtime.cpp
std::shared_ptr<Runtime> Runtime::get() {
...
    if(!theRuntimePtr__) {
        theRuntimePtr__ = new std::shared_ptr<Runtime>();
    }
    if (theRuntimePtr__) {
        if (!*theRuntimePtr__) {
            *theRuntimePtr__ = std::make_shared<Runtime>();
            // 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<IniFileReader::Section> 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

2.Runtime::buildProxy

传入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> proxy
        = createProxy(_domain,
                      ProxyClass_<AttributeExtensions_...>::getInterface(),
                      _instance,
                      _connectionId);

    if (proxy) {
        return std::make_shared<ProxyClass_<AttributeExtensions_...>>(proxy);
    }
    return nullptr;
}

ProxyClass_::getInterface(),

对应如下代码,根据FIDL文件自动生成的代码。

// XXX.h 
const char* Vehicle::getInterface() {
    return ("commonapi.examples.Vehicle:v1_2");
}

Runtime::createProxy

  1. initFactories初始化通讯协议实现相关的类
  2. 加载libCommonAPI-SomeIP.so
  3. createProxyHelper
//CommonAPI/Runtime.cpp
std::shared_ptr<Proxy>
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> proxy = createProxyHelper(_domain, _interface, _instance, _connectionId, false);
    if (!proxy) {
        // ...it seems do not, lets try to load a library that does...
        std::lock_guard<std::mutex> 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<std::mutex> 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> _factory) {
    bool isRegistered(false);
#ifndef _WIN32
    std::lock_guard<std::mutex> 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<Proxy>
Runtime::createProxyHelper(const std::string &_domain, const std::string &_interface, const std::string &_instance,
                           const std::string &_connectionId, bool _useDefault) {
    std::lock_guard<std::mutex> itsLock(factoriesMutex_);
    for (auto factory : factories_) {
        std::shared_ptr<Proxy> 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类

  1. CommonAPI address 转换为someipAddress
  2. 获取connection,没有则发起连接
  3. 构造Proxy
//CommonAPI/SomeIP/Factory.cpp
std::shared_ptr<CommonAPI::Proxy>
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<Connection> itsConnection
                = getConnection(_connectionId);
            if (itsConnection) {
                std::shared_ptr<Proxy> 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

  1. 如果连接已经建立,直接返回
  2. 如果未创建,构造Connection,并建立新的连接Connection->connect(true)
//CommonAPI/SomeIP/Factory.cpp
std::shared_ptr<Connection>
Factory::getConnection(const ConnectionId_t &_connectionId) {
    std::unique_lock<std::recursive_mutex> 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<Connection> itsConnection
            = std::make_shared<Connection>(_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)),

3.vsomeip::runtime_impl::create_application

//vsomeip/implementation/runtime/src/runtime_impl.cpp
std::shared_ptr<application> runtime_impl::create_application(
        const std::string &_name) {
    static std::uint32_t postfix_id = 0;
    std::lock_guard<std::mutex> 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> application = std::make_shared<application_impl>(its_name_);
    applications_[its_name_] = application;
    return application;
}

连接服务

  1. 初始化vsomeip application
  2. 注册初始化状态回调
// CommonAPI/SomeIP/Connection.cpp
bool Connection::connect(bool) {
    if (!application_->init())
        return false;

    std::function<void(state_type_e)> connectionHandler = std::bind(&Connection::onConnectionEvent,
                                                                    shared_from_this(),
                                                                    std::placeholders::_1);
    application_->register_state_handler(connectionHandler);

    asyncAnswersCleanupThread_ = std::make_shared<std::thread>(&Connection::cleanup, this);
    dispatchThread_ = std::make_shared<std::thread>(&Connection::dispatch, this);
    return true;
}

4.vsomeip::application_impl->init

// vsomeip/implementation/runtime/src/application_impl.cpp
bool application_impl::init() {
    
    //加载config
    
}  
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mongo-Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值