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

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-serviceclass haluser vehicle_networkgroup 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,用于存储VehiclePropValuemHidlVecOfVehiclePropValuePool.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 areaif (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//初始化VehiclePropValueVehiclePropValue 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;}//属性值写入VehiclePropertyStoremPropStore->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代码:

@startumlparticipant init
box
participant VehicleService
participant VehicleHalManager
participant VehicleEmulator
participant EmulatedVehicleHal
participant EmulatedVehicleConnector
participant VehiclePropertyStore
participant SocketComm
participant SubscriptionManager
participant DeathRecipient
participant BatchingConsumer
endboxinit -> 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()
loopBatchingConsumer -> 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.rhkb.cn/news/439777.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux的那些事】shell命名及Linux权限的理解

目录 一、shell命令以及运行原理 二、Linux权限的概念 三、Linux权限管理 3.1.文件访问者的分类&#xff08;人&#xff09; 3.2.文件类型和访问权限&#xff08;事物属性&#xff09; 3.3.文件权限值的表示方法 3.4.文件访问权限的相关设置方法 a)chmod b)chown c)…

【Spring Boot React】Spring Boot和React教程 完整版

【Spring Boot & React】Spring Boot和React教程 在B站找到一个不错的SpringBoot和React的学习视频&#xff0c;作者是amigoscode 【Spring Boot & React】Spring Boot和React教程 2023年更新版【Spring Boot React】价值79.9美元&#xff0c;全栈开发&#xff0c;搭…

Luminar激光雷达公司裁员重组的深度分析

在科技行业风起云涌的今天,每一家企业都面临着前所未有的挑战与机遇。当地时间9月23日,美国激光雷达领域的领军企业Luminar Technologies向美国证券交易委员会(SEC)提交了一份8-K报告,正式宣布了一项重大的业务重组计划,其核心内容是通过进一步裁员来优化成本结构,以期在…

windows上安装python环境

前言 最近电脑重装了系统&#xff0c;需要重新安装python环境 &#xff0c;因此记录一下 1.下载 打开python官网下载&#xff0c;下载链接&#xff1a;https://www.python.org/downloads/windows/ 点击下载 &#xff0c;我这里使用64位操作系统(大部分电脑)&#xff0c;根据…

快速上手C语言【上】(非常详细!!!)

目录 1. 基本数据类型 2. 变量 2.1 定义格式 和 命名规范 2.2 格式化输入和输出&#xff08;scanf 和 printf&#xff09; ​编辑 2.3 作用域和生命周期 3. 常量 4. 字符串转义字符注释 5. 操作符 5.1 双目操作符 5.1.1 算数操作符 5.1.2 移位操作符 5.1.3 位操作符…

为Floorp浏览器添加搜索引擎及搜索栏相关设置. 2024-10-05

Floorp浏览器开源项目地址: https://github.com/floorp-Projects/floorp/ 1.第一步 为Floorp浏览器添加搜索栏 (1.工具栏空白处 次键选择 定制工具栏 (2. 把 搜索框 拖动至工具栏 2.添加搜索引擎 以添加 搜狗搜索 为例 (1.访问 搜索引擎网址 搜狗搜索引擎 - 上网从搜狗开始 (2…

Java 网络编程基础

网络通信三要素 此笔记来之与黑马.B站的视频是真的高 基本的通信架构 基本的通信架构有2种形式&#xff1a;CS架构&#xff08;Client 客户端/ Server 服务端&#xff09;、BS架构( Browser 浏览器/ Server 服务端)。 IP 地址 IP&#xff08;InternetProtocol&#xff09;&a…

关于Zipf定律与TF—IDF的一个实践

在这篇文章中&#xff0c;我将通过机器学习中的线性回归来计算zipf定律中一个经验常数alpha&#xff0c;还会画TF-IDF的图像&#xff0c;此外还将简单介绍下与zipf、TF-IDF有关的知识。 在之前的一篇文章中我曾介绍过TF-IDF&#xff0c;但之后我又阅读了Ricardo Baeza-Yates和…

PELT算法

PELT算法的范畴 PELT算法&#xff08;Pruned Exact Linear Time&#xff09;属于时间序列分析和变点检测&#xff08;Change Point Detection&#xff09;范畴的算法。 从更广泛的角度来看&#xff0c;PELT算法还可以归类为以下几类算法的子集&#xff1a; 1. 时间序列分析&…

【数据结构】什么是红黑树(Red Black Tree)?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f4cc;红黑树的概念 &#x1f4cc;红黑树的操作 &#x1f38f;红黑树的插入操作 &#x1f38f;红黑树的删除操作 结语 &#x1f4cc;红黑树的概念 我们之前学过了…

codetop标签树刷题(四)!!暴打面试官!!!!

用于个人复习 1.二叉树的右视图2.二叉树最大宽度3.二叉树的最大深度4.N叉树的最大深度5.二叉树的最小深度6.子树的最大平均值7.求根节点到叶节点的数字之和8.另一棵树的子树9.对称二叉树 1.二叉树的右视图 给定一个二叉树的根节点root&#xff0c;想象自己站在它的右侧&#x…

TypeScript 封装 Axios 1.7.7

随着Axios版本的不同&#xff0c;类型也在改变&#xff0c;以后怎么写类型&#xff1f; yarn add axios1. 封装Axios 将Axios封装成一个类&#xff0c;同时重新封装request方法 重新封装request有几个好处&#xff1a; 所有的请求将从我们定义的requet请求中发送&#xff…

setTimeout,setInterval ,requestAnimationFrame定时器

setTimeout&#xff0c;setInterval &#xff0c;requestAnimationFrame定时器 定时器函数通常用于执行定时任务&#xff0c;也就是说你做了一个功能放在定时器函数里&#xff0c;它可以在特定的时间去执行你的指令&#xff0c;或者说隔多长时间&#xff08;单位时间内—毫秒为…

Centos Stream 9备份与恢复、实体小主机安装PVE系统、PVE安装Centos Stream 9

最近折腾小主机&#xff0c;搭建项目环境&#xff0c;记录相关步骤 数据无价&#xff0c;丢失难复 1. Centos Stream 9备份与恢复 1.1 系统备份 root权限用户执行进入根目录&#xff1a; cd /第一种方式备份命令&#xff1a; tar cvpzf backup.tgz / --exclude/proc --exclu…

计算机系统基础概述

什么是计算机&#xff1f; 计算机是一种利用电子技术进行信息处理的设备&#xff0c;它能够接收、存储、处理和提供数据。计算机通过执行一系列预定义的指令来处理数据&#xff0c;这些指令通常被称为程序。计算机的核心功能包括算术运算、逻辑判断、数据存储和信息检索 计算…

STM32 通用定时器

一、概述 STM32内部集成了多个定时/计数器&#xff0c;根据型号不同&#xff0c;STM32系列芯片最多包含8个定时/计数器。其中&#xff0c;TIM6、TIM7为基本定时器&#xff0c;TIM2~TIM5为通用定时器&#xff0c;TIM1、TIM8为高级控制定时器。 1.定时器的类型 基本定时器通用定…

微信小程序-npm支持-如何使用npm包

文章目录 1、在内建终端中打开2、npm init -y3、Vant Weapp4、通过 npm 安装5、构建 npm 1、在内建终端中打开 Windows PowerShell 版权所有 (C) Microsoft Corporation。保留所有权利。尝试新的跨平台 PowerShell https://aka.ms/pscore6PS C:\Users\dgq\WeChatProjects\minip…

python泵站设备运行预警信息管理系统

目录 功能介绍具体实现截图技术栈和环境说明python语言解决的思路性能/安全/负载方面核心代码部分展示详细视频演示源码获取方式 功能介绍 用户端 注册登录&#xff1a;用户可以注册账号并登录系统。 西电泵站简介&#xff1a;提供泵站的历史、功能和重要性等详细介绍。 泵站…

余承东直播论道智能驾驶:激光雷达不可或缺,华为ADS 3.0引领安全创新

华为余承东:激光雷达,智能驾驶安全性的关键 9月29日,华为消费者业务集团CEO余承东在一场引人注目的直播中,与知名主持人马东就智能驾驶技术的最新进展进行了深入交流。在这场直播中,余承东针对激光雷达在智能驾驶中的必要性问题,发表了明确且深刻的观点,引发了业界和公众…

在Docker中运行微服务注册中心Eureka

1、Docker简介&#xff1a; 作为开发者&#xff0c;经常遇到一个头大的问题&#xff1a;“在我机器上能运行”。而将SpringCloud微服务运行在Docker容器中&#xff0c;避免了因环境差异带来的兼容性问题&#xff0c;能够有效的解决此类问题。 通过Docker&#xff0c;开发者可…