Android车载——VehicleHal初始化(Android 11)

news2024/11/28 14:37:42

1 概述

VehicleHal是AOSP中车辆服务相关的hal层服务。它主要定义了与汽车硬件交互的标准化接口和属性管理,是一个独立的进程。

2 进程启动

VehicleHal相关代码在源码树中的hardware/interfaces/automotive目录下
首先看下Android.bp文件:

cc_binary {
    name: "android.hardware.automotive.vehicle@2.0-service",
    defaults: ["vhal_v2_0_target_defaults"],
    vintf_fragments: [
        "android.hardware.automotive.vehicle@2.0-service.xml",
    ],
    init_rc: ["android.hardware.automotive.vehicle@2.0-service.rc"],
    vendor: true,
    relative_install_path: "hw",
    srcs: ["VehicleService.cpp"],
    shared_libs: [
        "libbase",
        "libjsoncpp",
        "libprotobuf-cpp-lite",
    ],
    static_libs: [
        "android.hardware.automotive.vehicle@2.0-manager-lib",
        "android.hardware.automotive.vehicle@2.0-default-impl-lib",
        "android.hardware.automotive.vehicle@2.0-libproto-native",
        "libqemu_pipe",
    ],
}

标准的hal服务层定义,入口在VehicleService.cpp,其他依赖文件在static_libs中定义。服务的可执行文件编译完成之后的名称是android.hardware.automotive.vehicle@2.0-service。

进程是hal服务进程,由init通过解析rc文件进行拉起

service vendor.vehicle-hal-2.0 /vendor/bin/hw/android.hardware.automotive.vehicle@2.0-service
    class hal
    user vehicle_network
    group system inet

进程名vendor.vehicle-hal-2.0,执行的就是/vendor/bin/hw/android.hardware.automotive.vehicle@2.0-service这个可执行文件,class为hal,用户是vehicle_network,用户组是system和inet。
在init中class_start hal的时候启动该hal进程。

3 VHAL初始化

VHAL进程的入口在VehicleService.cpp中的main函数
hardware/interfaces/automotive/vehicle/2.0/default/VehicleService.cpp

// xy:VHAL的入口函数,由init进程启动
int main(int /* argc */, char* /* argv */ []) {
    // xy:缓存属性值的地方
    auto store = std::make_unique<VehiclePropertyStore>();
    // xy:模拟与真实车辆的连接
    auto connector = std::make_unique<impl::EmulatedVehicleConnector>();
    // xy:模拟Hal,Hal的具体实现
    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
    // xy:汽车模拟类,模拟车辆信号
    auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
    // xy:VHAL的服务实现入口
    auto service = std::make_unique<VehicleHalManager>(hal.get());
    // xy:设置存储属性值的池子,便于重复使用
    connector->setValuePool(hal->getValuePool());

    // xy:设置binder线程数量
    configureRpcThreadpool(4, false /* callerWillJoin */);

    ALOGI("Registering as service...");
    // xy:将当前服务注册到HwServiceManager中
    status_t status = service->registerAsService();

    if (status != OK) {
        ALOGE("Unable to register vehicle service (%d)", status);
        return 1;
    }

    // Setup a binder thread pool to be a car watchdog client.
    // xy:watchDog设置
    ABinderProcess_setThreadPoolMaxThreadCount(1);
    ABinderProcess_startThreadPool();
    sp<Looper> looper(Looper::prepare(0 /* opts */));
    std::shared_ptr<WatchdogClient> watchdogClient =
            ndk::SharedRefBase::make<WatchdogClient>(looper, service.get());
    // The current health check is done in the main thread, so it falls short of capturing the real
    // situation. Checking through HAL binder thread should be considered.
    if (!watchdogClient->initialize()) {
        ALOGE("Failed to initialize car watchdog client");
        return 1;
    }
    ALOGI("Ready");
    while (true) {
        looper->pollAll(-1 /* timeoutMillis */);
    }

    return 1;
}

接下来逐步解析各个模块的初始化

3.1 VehiclePropertyStore初始化

VehiclePropertyStore类的主要职责是缓存车辆数据,采用默认构造函数,构造函数中没有初始化逻辑。

using PropertyMap = std::map<RecordId, VehiclePropValue>;
std::unordered_map<int32_t /* VehicleProperty */, RecordConfig> mConfigs;
PropertyMap mPropertyValues;  // Sorted map of RecordId : VehiclePropValue.

主要初始化了这两个数据对象,其中mConfigs用于存储属性配置,mPropertyValues用于存储属性值。

3.2 EmulatedVehicleConnector初始化

也是采用无参构造,初始化了一个对象

EmulatedUserHal mEmulatedUserHal;

3.3 EmulatedVehicleHal初始化

EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client,
                       EmulatedUserHal* emulatedUserHal = nullptr);

这个类只有一个三个参数的构造函数,第三个参数有默认值,其实现如下:

EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client,
                                       EmulatedUserHal* emulatedUserHal)
    : mPropStore(propStore),
      mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
      mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this,
                                std::placeholders::_1)),
      mVehicleClient(client),
      mEmulatedUserHal(emulatedUserHal) {
    initStaticConfig();
    for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
        mPropStore->registerProperty(kVehicleProperties[i].config);
    }
    mVehicleClient->registerPropertyValueCallback(std::bind(&EmulatedVehicleHal::onPropertyValue,
                                                            this, std::placeholders::_1,
                                                            std::placeholders::_2));
}

这个构造函数初始化的时候传入的两个参数是在main函数中创建的VehiclePropertyStore对象和EmulatedVehicleConnector对象,而这个构造函数的第二个参数却是VehicleHalClient,这是怎么回事呢?

class EmulatedVehicleConnector : public IPassThroughConnector<VehicleHalClient, VehicleHalServer>

template <typename VehicleClientType, typename VehicleServerType>
class IPassThroughConnector : public VehicleClientType, public VehicleServerType

从上面可以看出,EmulatedVehicleConnector继承自IPassThroughConnector,而IPassThroughConnector定义了两个模板,IPassThroughConnector继承这两个模板类。所以EmulatedVehicleConnector继承VehicleHalClient和VehicleHalServer。所以EmulatedVehicleConnector是VehicleHalClient的子类。
接着分析EmulatedVehicleHal的构造函数,这里用传入的VehiclePropertyStore对象初始化mPropStore。

std::unordered_set<int32_t> mHvacPowerProps;

const int32_t kHvacPowerProperties[] = {
    toInt(VehicleProperty::HVAC_FAN_SPEED),
    toInt(VehicleProperty::HVAC_FAN_DIRECTION),
};

mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties))

然后初始化这个成员变量,将数组中的两个空调相关的property的propId添加到mHvacPowerProps这个vector中。

RecurrentTimer mRecurrentTimer;

mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this,
                                std::placeholders::_1)),

RecurrentTimer(const Action& action) : mAction(action) {
        mTimerThread = std::thread(&RecurrentTimer::loop, this, action);
    }

这个是一个执行定时任务相关的类,初始化成员变量mRecurrentTimer为一个RecurrentTimer对象,这个对象在初始化的时候会创建一个线程,这个线程中会定时执行传入的函数。具体的分析见3.7小结。

mVehicleClient(client)

然后初始化mVehicleClient为main函数中创建的EmulatedVehicleConnector对象。

mEmulatedUserHal(emulatedUserHal)

这个使用默认参数,空指针。

void EmulatedVehicleHal::initStaticConfig() {
    for (auto&& it = std::begin(kVehicleProperties); it != std::end(kVehicleProperties); ++it) {
        const auto& cfg = it->config;
        VehiclePropertyStore::TokenFunction tokenFunction = nullptr;

        switch (cfg.prop) {
            case OBD2_FREEZE_FRAME: {
                tokenFunction = [](const VehiclePropValue& propValue) {
                    return propValue.timestamp;
                };
                break;
            }
            default:
                break;
        }

        mPropStore->registerProperty(cfg, tokenFunction);
    }
}

然后初始化属性配置,kVehicleProperties是一个定义了车辆属性配置的结构体数组,以下是其中的一个元素,表示车辆的空调温度设置的属性配置:

{.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
            .access = VehiclePropertyAccess::READ_WRITE,
            .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
            .areaConfigs = {VehicleAreaConfig{
                                    .areaId = HVAC_LEFT,
                                    .minFloatValue = 16,
                                    .maxFloatValue = 32,
                            },
                            VehicleAreaConfig{
                                    .areaId = HVAC_RIGHT,
                                    .minFloatValue = 16,
                                    .maxFloatValue = 32,
                            }}},
 .initialAreaValues = {{HVAC_LEFT, {.floatValues = {16}}},
                       {HVAC_RIGHT, {.floatValues = {20}}}}},

OBD2_FREEZE_FRAME表示冻结帧,跟诊断相关,暂时不清楚,暂不看这块的处理。然后会将所有的属性配置注册到VehiclePropertyStore中。
这些属性配置就是vhal中支持的属性,如果没有在这个结构体数组中定义,则该功能不支持,供应商提供的新的需要在这个结构体中新增。
后面的for循环和initStaticConfig中的逻辑一样,跳过,这块应该没什么意义的。

mVehicleClient->registerPropertyValueCallback(std::bind(&EmulatedVehicleHal::onPropertyValue,
                                                            this, std::placeholders::_1,
                                                            std::placeholders::_2));

最后注册callback函数到EmulatedVehicleConnector对象中,回调函数是EmulatedVehicleHal::onPropertyValue。
至此,就初始化完成了,主要做的就是创建EmulatedVehicleHal对象,并且注册一些回调函数,然后比较重要的一点是加载了所有的属性配置到VehiclePropertyStore中。

3.4 VehicleEmulator初始化

VehicleEmulator::VehicleEmulator(EmulatedVehicleHalIface* hal) : mHal{hal} {
    mHal->registerEmulator(this);

    ALOGI("Starting SocketComm");
    mSocketComm = std::make_unique<SocketComm>(this);
    mSocketComm->start();

    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
        ALOGI("Starting PipeComm");
        mPipeComm = std::make_unique<PipeComm>(this);
        mPipeComm->start();
    }
}

持有EmulatedVehicleHal对象,创建SocketComm或者PipeComm,并启动,这是模拟的与VHAL连接的客户端的通信类。

3.5 VehicleHalManager初始化

VehicleHalManager(VehicleHal* vehicleHal)
        : mHal(vehicleHal),
          mSubscriptionManager(std::bind(&VehicleHalManager::onAllClientsUnsubscribed,
                                         this, std::placeholders::_1)) {
        init();
    }

先将EmulatedVehicleHal保存至mHal变量中,然后会初始化一个订阅相关的类SubscriptionManager,最后调用init函数。
SubscriptionManager初始化见3.6小节,接下来分析init函数

hidl_vec<VehiclePropValue> mHidlVecOfVehiclePropValuePool;
------------------------------------------------------------------------------------------------------------------------
void VehicleHalManager::init() {
    ALOGI("VehicleHalManager::init");

	//初始化mHidlVecOfVehiclePropValuePool为20,用于存储VehiclePropValue
    mHidlVecOfVehiclePropValuePool.resize(kMaxHidlVecOfVehiclPropValuePoolSize);

	//批处理相关的初始化
    mBatchingConsumer.run(&mEventQueue,
                          kHalEventBatchingTimeWindow,
                          std::bind(&VehicleHalManager::onBatchHalEvent,
                                    this, _1));
	//事件处理相关初始化
    mHal->init(&mValueObjectPool,
               std::bind(&VehicleHalManager::onHalEvent, this, _1),
               std::bind(&VehicleHalManager::onHalPropertySetError, this,
                         _1, _2, _3));

    // Initialize index with vehicle configurations received from VehicleHal.
    auto supportedPropConfigs = mHal->listProperties();
    mConfigIndex.reset(new VehiclePropConfigIndex(supportedPropConfigs));

    std::vector<int32_t> supportedProperties(
        supportedPropConfigs.size());
    //
    for (const auto& config : supportedPropConfigs) {
        supportedProperties.push_back(config.prop);
    }
}

VehiclePropConfigIndex初始化见3.7小节

3.5.1 批处理初始化

ConcurrentQueue<VehiclePropValuePtr> mEventQueue;
constexpr std::chrono::milliseconds kHalEventBatchingTimeWindow(10);
------------------------------------------------------------------------------------------------------------------------
mBatchingConsumer.run(&mEventQueue,
                      kHalEventBatchingTimeWindow,
                      std::bind(&VehicleHalManager::onBatchHalEvent,
                                this, _1));
------------------------------------------------------------------------------------------------------------------------
void run(ConcurrentQueue<T>* queue,
         std::chrono::nanoseconds batchInterval,
         const OnBatchReceivedFunc& func) {
    mQueue = queue;
    mBatchInterval = batchInterval;

    mWorkerThread = std::thread(
        &BatchingConsumer<T>::runInternal, this, func);
}

先看这部分代码,mQueue=mEventQueue,用于添加事件,是VehiclePropValuePtr类型事件。mBatchInterval=kHalEventBatchingTimeWindow=10,然后创建了一个线程,执行的函数是BatchingConsumer::runInternal,传入的参数是VehicleHalManager::onBatchHalEvent。

void runInternal(const OnBatchReceivedFunc& onBatchReceived) {
        if (mState.exchange(State::RUNNING) == State::INIT) {
            while (State::RUNNING == mState) {
                mQueue->waitForItems();
                if (State::STOP_REQUESTED == mState) break;

                std::this_thread::sleep_for(mBatchInterval);
                if (State::STOP_REQUESTED == mState) break;

                std::vector<T> items = mQueue->flush();

                if (items.size() > 0) {
                    onBatchReceived(items);
                }
            }
        }

        mState = State::STOPPED;
    }

批处理这个类的主要作用就是循环执行mQueue中的事件,如果有事件到来就执行,没有就休眠。mQueue事件什么时候添加后续分析。

3.5.2 初始化现有属性值

VehiclePropValuePool mValueObjectPool;

VehiclePropValuePool(size_t maxRecyclableVectorSize = 4) :
        mMaxRecyclableVectorSize(maxRecyclableVectorSize) {};
----------------------------------------------------------------------------------------------
mHal->init(&mValueObjectPool,
           std::bind(&VehicleHalManager::onHalEvent, this, _1),
           std::bind(&VehicleHalManager::onHalPropertySetError, this,
                     _1, _2, _3));
----------------------------------------------------------------------------------------------
void init(
    VehiclePropValuePool* valueObjectPool,
    const HalEventFunction& onHalEvent,
    const HalErrorFunction& onHalError) {
    mValuePool = valueObjectPool;
    mOnHalEvent = onHalEvent;
    mOnHalPropertySetError = onHalError;

    onCreate();
}

mValuePool存储的是VehiclePropValuePool对象,是用于VehiclePropValue解析的池子,方便循环利用。mOnHalEvent是onHalEvent函数,mOnHalPropertySetError是onHalPropertySetError函数,然后调用onCreate函数。onCreate是一个虚函数,由实际的VehicleHal类实现,即EmulatedVehicleHal中的实现:

// Parse supported properties list and generate vector of property values to hold current values.
void EmulatedVehicleHal::onCreate() {
    static constexpr bool shouldUpdateStatus = true;

	//遍历所有的属性配置
    for (auto& it : kVehicleProperties) {
        VehiclePropConfig cfg = it.config;
        int32_t numAreas = cfg.areaConfigs.size();

        if (isDiagnosticProperty(cfg)) {
            // do not write an initial empty value for the diagnostic properties
            // as we will initialize those separately.
            continue;
        }

        // A global property will have only a single area
        if (isGlobalProp(cfg.prop)) {
            numAreas = 1;
        }

		//对于分区属性的处理
        for (int i = 0; i < numAreas; i++) {
            int32_t curArea;

            if (isGlobalProp(cfg.prop)) {
                curArea = 0;
            } else {
                curArea = cfg.areaConfigs[i].areaId;
            }

            // Create a separate instance for each individual zone
            //初始化VehiclePropValue
            VehiclePropValue prop = {
                    .areaId = curArea,
                    .prop = cfg.prop,
            };
			
			//设置初始属性值
            if (it.initialAreaValues.size() > 0) {
                auto valueForAreaIt = it.initialAreaValues.find(curArea);
                if (valueForAreaIt != it.initialAreaValues.end()) {
                    prop.value = valueForAreaIt->second;
                } else {
                    ALOGW("%s failed to get default value for prop 0x%x area 0x%x",
                            __func__, cfg.prop, curArea);
                }
            } else {
                prop.value = it.initialValue;
            }
            //属性值写入VehiclePropertyStore
            mPropStore->writeValue(prop, shouldUpdateStatus);
        }
    }
    initObd2LiveFrame(*mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
    initObd2FreezeFrame(*mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
}

这块主要就是将根据默认配置里面的属性配置,将初始化的属性值写入到VehiclePropertyStore中进行缓存。

3.6 SubscriptionManager初始化

SubscriptionManager是在VehicleHalManager中创建并持有的。

mSubscriptionManager(std::bind(&VehicleHalManager::onAllClientsUnsubscribed,
                     this, std::placeholders::_1))
----------------------------------------------------------------------------------------------
SubscriptionManager(const OnPropertyUnsubscribed& onPropertyUnsubscribed)
        : mOnPropertyUnsubscribed(onPropertyUnsubscribed),
            mCallbackDeathRecipient(new DeathRecipient(
                std::bind(&SubscriptionManager::onCallbackDead, this, std::placeholders::_1)))
{}

传入的参数是一个函数,保存在mOnPropertyUnsubscribed中,并初始化mCallbackDeathRecipient为一个DeathRecipient对象,这个对象构造时的参数为onCallbackDead函数。

DeathRecipient(const OnClientDead& onClientDead)
            : mOnClientDead(onClientDead) {}

onCallbackDead保存在mOnClientDead中。
VehicleHalManager中创建SubscriptionManager对象,并对其进行管理。

3.7 RecurrentTimer初始化

RecurrentTimer由EmulatedVehicleHal的构造函数初始化,并适时回调EmulatedVehicleHal中的回调函数。

RecurrentTimer mRecurrentTimer;

mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this,
                                std::placeholders::_1)),

RecurrentTimer(const Action& action) : mAction(action) {
        mTimerThread = std::thread(&RecurrentTimer::loop, this, action);
    }

using Action = std::function<void(const std::vector<int32_t>& cookies)>;

接着3.3节中分析,RecurrentTimer对象创建后,赋值给mRecurrentTimer。RecurrentTimer创建时,传入的参数是一个function类型数据,包含的是一个函数。EmulatedVehicleHal::onContinuousPropertyTimer,bind函数的第二个参数传入this,因为是一个成员函数,然后是一个参数占位符,因为该函数需要传入一个参数。
然后RecurrentTimer的构造函数中,创建了一个线程,执行的函数是RecurrentTimer::loop,传入的参数是Action对象,即onContinuousPropertyTimer

void loop(const Action& action) {
    static constexpr auto kInvalidTime = TimePoint(Nanos::max());

    std::vector<int32_t> cookies;

    while (!mStopRequested) {
        auto now = Clock::now();
        auto nextEventTime = kInvalidTime;
        cookies.clear();

        {
            std::unique_lock<std::mutex> g(mLock);

            for (auto&& it : mCookieToEventsMap) {
            	//获取定时上报事件
                RecurrentEvent& event = it.second;
                if (event.absoluteTime <= now) {
                    event.updateNextEventTime(now);
                    cookies.push_back(event.cookie);
                }

                if (nextEventTime > event.absoluteTime) {
                    nextEventTime = event.absoluteTime;
                }
            }
        }

        if (cookies.size() != 0) {
            action(cookies);
        }

        std::unique_lock<std::mutex> g(mLock);
        mCond.wait_until(g, nextEventTime);  // nextEventTime can be nanoseconds::max()
    }
}

mStopRequested没有其他地方赋值,有默认值为false,所以会进入while循环。
这里主要是定时事件上报的处理逻辑,如果到时间了,就会加入到cookies这个变量中,并调用action这个回到函数,即onContinuousPropertyTimer这个回调函数去处理所有到时间的定时事件。

4 初始化流程图

plantuml代码:

@startuml

participant init
box
participant VehicleService
participant VehicleHalManager
participant VehicleEmulator
participant EmulatedVehicleHal
participant EmulatedVehicleConnector
participant VehiclePropertyStore
participant SocketComm
participant SubscriptionManager
participant DeathRecipient
participant BatchingConsumer
endbox

init -> VehicleService: 拉起服务
VehicleService -> VehiclePropertyStore: new VehiclePropertyStore()
VehicleService -> EmulatedVehicleConnector: new EmulatedVehicleConnector()
VehicleService -> EmulatedVehicleHal: new EmulatedVehicleHal(VehiclePropertyStore* propStore, \n\tVehicleHalClient* client, EmulatedUserHal* emulatedUserHal = nullptr);
EmulatedVehicleHal -> EmulatedVehicleHal: initStaticConfig()
EmulatedVehicleHal -> VehiclePropertyStore: registerProperty(const VehiclePropConfig& config, \n\tVehiclePropertyStore::TokenFunction tokenFunc)
EmulatedVehicleHal -> EmulatedVehicleConnector: registerPropertyValueCallback(PropertyCallBackType&& callback)
VehicleService -> VehicleEmulator: new VehicleEmulator(EmulatedVehicleHalIface* hal)
VehicleEmulator -> EmulatedVehicleHal: registerEmulator(this)
VehicleEmulator -> SocketComm: start()
VehicleService -> VehicleHalManager: new VehicleHalManager(VehicleHal* vehicleHal)
VehicleHalManager -> SubscriptionManager: new (const OnPropertyUnsubscribed& onPropertyUnsubscribed\n\t: mOnPropertyUnsubscribed(onPropertyUnsubscribed),\n\tmCallbackDeathRecipient(new DeathRecipient(\n\tstd::bind(&SubscriptionManager::onCallbackDead, this, std::placeholders::_1)))
SubscriptionManager -> DeathRecipient: new DeathRecipient(const OnClientDead& onClientDead)
VehicleHalManager -> VehicleHalManager: init()
VehicleHalManager -> BatchingConsumer: run()
loop
    BatchingConsumer -> BatchingConsumer: runInternal(const OnBatchReceivedFunc& onBatchReceived)
end loop
VehicleHalManager -> EmulatedVehicleHal: init( \n\tVehiclePropValuePool* valueObjectPool, \n\tconst HalEventFunction& onHalEvent, \n\tconst HalErrorFunction& onHalError)
EmulatedVehicleHal -> EmulatedVehicleHal: onCreate()
EmulatedVehicleHal -> VehiclePropertyStore: writeValue(const VehiclePropValue& propValue, bool updateStatus)
VehicleService -> EmulatedVehicleConnector: setValuePool(VehiclePropValuePool* valuePool)
VehicleService -> VehicleHalManager: registerAsService()
@enduml

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2192294.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大模型公司对标:360

公司档案 360成立于2005年&#xff0c;初期以提供免费的杀毒软件“360安全卫士”而迅速获得市场认可&#xff0c;并逐渐发展成为一家提供全面互联网安全解决方案的企业。2015年成立人工智能研究院&#xff0c;开展人工智能技术探索&#xff0c;成为国内布局研究开发人工智能较…

Oracle 表空间异构传输

已经有了表空间的数据文件&#xff0c;和元数据dump文件&#xff0c;如何把这个表空间传输到异构表空间中&#xff1f; 查询异构传输平台信息&#xff1a; COLUMN PLATFORM_NAME FORMAT A40 SELECT PLATFORM_ID, PLATFORM_NAME, ENDIAN_FORMAT FROM V$TRANSPORTABLE_PLATFORM O…

教育技术革新:SpringBoot在线教育系统开发指南

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

计算机找不到vcomp140.dll,无法继续执行代码如何解决,有什么好的修复方法

1. vcomp140.dll 简介 1.1 定义 vcomp140.dll 是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;它属于 Microsoft Visual C 2015 Redistributable Package 的一部分。该文件为应用程序提供了 OpenMP 并行框架所需的运行时支持&#xff0c;允许开发者编写并发和多…

【Verilog学习日常】—牛客网刷题—Verilog进阶挑战—VL25

输入序列连续的序列检测 描述 请编写一个序列检测模块&#xff0c;检测输入信号a是否满足01110001序列&#xff0c;当信号满足该序列&#xff0c;给出指示信号match。 模块的接口信号图如下&#xff1a; 模块的时序图如下&#xff1a; 请使用Verilog HDL实现以上功能&#x…

论文笔记:微表情欺骗检测

整理了AAAI2018 Deception Detection in Videos 论文的阅读笔记 背景模型实验可视化 背景 欺骗在我们的日常生活中很常见。一些谎言是无害的&#xff0c;而另一些谎言可能会产生严重的后果。例如&#xff0c;在法庭上撒谎可能会影响司法公正&#xff0c;让有罪的被告逍遥法外。…

电脑获得高级管理员权限(Windows10 专业版)

电脑获得高级管理员权限(Windows10 专业版) 请谨慎操作 通常我们在删除一些文件时&#xff0c;会提示权限不足&#xff0c;删除不了文件 我们可以打开组策略编辑器将当前用户修改为高级管理员权限 Windows10获取高级管理员权限 首先打开本地组策略编辑器(cmd输入gpedit.msc)其…

20分钟写一个链表

目录 前言1.带头结点的循环双链表1.1 链表的分类、线性表的对比1.2 双链表基本操作代码实现1.2.1 初始化1.2.2 销毁、打印链表 总结 前言 有一个学长在面试的时候被问到这样一个问题&#xff0c;“你可以用20分钟写一个链表吗&#xff1f;”学长第一反应是&#xff0c;至少要一…

传统图像处理Opencv分割不同颜色的夹子

任务要求&#x1f349; 1. 计算图像中夹子的总数。 2. 分别计算不同颜色夹子的个数。 3. 使用以下方法适应三张图片&#xff0c;并在每张图像上显示结果&#xff1a; - 阈值方法 - HSV颜色空间 - 连通域分析 - 形态学图像处理 - Canny边缘检测 4. 在结果中显示计…

北交大研究突破:塑料光纤赋能低成本无摄像头AR/VR眼动追踪技术

北交大研究&#xff1a;探索无摄像头低成本AR/VR眼动追踪新路径 在AR/VR技术领域&#xff0c;眼动追踪作为一项关键技术&#xff0c;对于提升用户体验、优化渲染效率具有重要意义。然而&#xff0c;传统的眼动追踪方案多依赖于高成本的摄像头&#xff0c;这不仅增加了设备的制造…

学习资料库系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;观看记录管理&#xff0c;基础数据管理&#xff0c;论坛信息管理&#xff0c;公告信息管理&#xff0c;轮播图信息 微信端账号功能包括&#xff1a;系统首页&#xff0c;阅读资…

性能学习5:性能测试的流程

一.需求分析 二.性能测试计划 1&#xff09;测什么&#xff1f; - 项目背景 - 测试目的 - 测试范围 - ... 2&#xff09;谁来测试 - 时间进度与分工 - 交付清单 - ... 3&#xff09;怎么测 - 测试策略 - ... 三.性能测试用例 四.性能测试执行 五.性能分析和调优 六…

【cpp/c++ summary 工具】 conan包管理器安装与cmake项目配置

发现有些包cvpkg中没有&#xff08;比如字典树&#xff09;&#xff0c;conan里有。 安装conan https://conan.io/downloads 配置conan PS C:\Users\multisim> conan config home C:\Users\multisim\.conan2 # Conan 主文件夹的路径,https://docs.conan.io/2/referen…

【梯级水电站调度优化】基于自适应权值优化粒子群算法

课题名称&#xff1a; 基于改进粒子群算法的梯级水电站调度优化 改进方向&#xff1a;自适应权值优化 代码获取方式&#xff08;付费&#xff09;&#xff1a; 相关资料&#xff1a; 1. 粒子群算法的基本原理 2. 梯级水电站调度优化模型 3. 代码注释 4. 代码讲解视频&am…

Python编程常用的35个经典案例

Python 的简洁和强大使其成为许多开发者的首选语言。本文将介绍35个常用的Python经典代码案例。这些示例覆盖了基础语法、常见任务、以及一些高级功能。 1.列表推导式 这个例子展示了列表推导式&#xff0c;用于生成FizzBuzz序列。 fizz_buzz_list ["FizzBuzz" i…

互联网Java工程师面试题及答案整理(2024年最新版)

前言 作为一个 Java 程序员&#xff0c;你平时总是陷在业务开发里&#xff0c;每天噼里啪啦忙敲着代码&#xff0c;上到系统开发&#xff0c;下到 Bug 修改&#xff0c;你感觉自己无所不能。然而偶尔的一次聚会&#xff0c;你听说和自己一起出道的同学早已经年薪 50 万&#x…

拥抱大模型时代,从入门到精通,科技从业者的转型攻略与AI红利指南

前言 自2022年11月ChatGPT问世以来&#xff0c;大模型在人工智能领域的潜能得到了充分展示&#xff0c;其深远影响引发了研究热潮。在不到一年的时间里&#xff0c;国产大模型如雨后春笋般涌现&#xff0c;2023年成为了大模型时代的元年。对于IT圈的科技从业者来说&#xff0c…

NextViT实战:使用NextViT实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

从零开始讲PCIe(8)——PCIe拓扑与组件

一、拓扑结构 和PCI-X一样&#xff0c;PCIe链路是点对点的连接&#xff0c;而不是像传统PCI那样使用共享总线。这是由于其使用了非常高的传输速度。因此&#xff0c;链路只能连接两个接口&#xff0c;为了构建一个复杂的系统&#xff0c;必须采用某种方式来扩展连接。在PCIe中&…

《Linux从小白到高手》理论篇:一文概览常用Linux重要配置文件

List item 今天继续宅家&#xff0c;闲来无事接着写。本篇是《Linux从小白到高手》理论篇的最后一篇了。本篇集中介绍所有常用的Linux重要配置文件。 用这个命令可以查看配置文件所在的位置&#xff1a;如上图 locate "*.conf" "*.ini" "*.cfg&quo…