【HarmonyOS】鸿蒙应用低功耗蓝牙BLE的使用心得 (二)

【HarmonyOS】鸿蒙应用低功耗蓝牙BLE的使用心得 (二)

一、前言

目前鸿蒙应用的实现逻辑,基本都是参考和移植Android端来实现。针对BLE低功耗蓝牙来说,在鸿蒙化的实现过程中。我们发现了,鸿蒙独有的优秀点,也发现了不一致带来的问题。

并且因为鸿蒙系统还在迭代中,难免有一些bug的存在。目前BLE低功耗蓝牙主流程环节是没有问题的。

在这里插入图片描述

鸿蒙整体的实现流程与Android基本是一致。但是在BLE中有一个点需要特别注意。

当周边设备(外围设备)开启广播时,其实没有前置条件,不需要先开启GATT服务器和注册相关服务。但是有些业务逻辑是如此,导致我们开发的时候,惯性思维。实际上鸿蒙BLE在广播流程设计上,并不需要前置条件。

当周边设备持续发送广播,例如智能腕表发送广播,这时候中心设备(中央设备),例如手机才能通过BLE扫描去去识别到该设备。然后才是连接和信息传递的过程。

二、目前已知鸿蒙和Android不一致的实现点梳理:

1.安卓中设置低功耗蓝牙广播,其中有参数可以设置超时时间:
AdvertiseSettings.Builder.SetTimeout()。
但是对标鸿蒙中设置广播,文档中并没有该参数。鸿蒙的低功耗蓝牙广播,无法设置超时时间。

2.鸿蒙中的低功耗蓝牙广播设置对象,ble.AdvertiseSetting中只能设置txPower广播强度。设置广播的模式,对标Android的三个广播模式:

在均衡电源模式下执行蓝牙LE广播:
AdvertiseSettings#ADVERTISE_MODE_BALANCED

在低延迟,高功率模式下执行蓝牙LE广播:
AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY

在低功耗模式下执行蓝牙LE广播
:AdvertiseSettings#ADVERTISE_MODE_LOW_POWER

鸿蒙这边可以使用interval参数来设置广播频率
ADVERTISE_MODE_LOW_LATENCY(低延迟,广播间隔100ms)对应interval的值为 160
ADVERTISE_MODE_BALANCED(均衡模式,广播间隔250ms)对应interval的值为 400
ADVERTISE_MODE_LOW_POWER(低功耗,广播间隔1s)对应interval的值为 1600

三、BLE低功耗蓝牙DEMO项目示例参考:

此项目实际上是从官方文档基础上进行了扩展。文档所示例的样本代码比较割裂,只是针对API功能的讲解,在不知道BLE完成业务流程的基础上,我们是不清楚如何调用的。所以我们开玩笑的角度说,基本上当你会这个技术点了,官方文档也就能看懂了。

项目整个框架采用管理对象进行BLE功能的封装:

GattServermanager
作为GATT服务器(周边设备)的管理类,实现服务器的初始化和服务的注册。

GattClientManager
作为周边设备客户端管理类,实现链接和数据读写传递。

BleScanManager
作为BLE扫描管理类,实现扫描和结果的处理。

BleAdvertisingManager
作为BLE广播类,实现广播的处理,特征值和描述符的生成。

BLEMgr
作为总管理类,业务逻辑的感知层,业务直接调用BLEMgr实现低功耗蓝牙接口的调用。

在这里插入图片描述
Index.ets 启动页

import { CommonTextModifier } from '../common/CommonTextModifier'
import { BLEMgr } from '../mgr/BLEMgr';
import { promptAction } from '@kit.ArkUI';
import { PermissionsUtil } from '../utils/PermissionsUtil';

struct Index { isOpenBluetooth: boolean = false;private mBLEMgr: BLEMgr = new BLEMgr();txtModifier: CommonTextModifier = new CommonTextModifier()async aboutToAppear() {let isHave: boolean = await PermissionsUtil.requestPermission();if(isHave){this.isOpenBluetooth = this.mBLEMgr.getBluetoothState();}else{this.toSysSettingPage();}}private toSysSettingPage(){globalThis.sysContext.startAbility({bundleName: 'com.huawei.hmos.settings',abilityName: 'com.huawei.hmos.settings.MainAbility',// com.huawei.hmos.settings.AppInfoAbilityuri: 'application_info_entry', //application_settings   application_info_entryparameters: {pushParams: globalThis.sysContext.abilityInfo.bundleName // 应用包名com.example.tosettingdemo  'uiAbilityContext.abilityInfo.bundleName'}});}onClickStart = async ()=>{let isHave: boolean = await PermissionsUtil.requestPermission();if(isHave){this.mBLEMgr.startBluetooth((str: string)=>{let content: string = "";if (str == 'STATE_ON') {content = "蓝牙已开启";}else{content = "开启错误:" + str;}promptAction.showToast({message: content});});}else{this.toSysSettingPage();}}onClickClose = async ()=>{let isHave: boolean = await PermissionsUtil.requestPermission();if(isHave){this.mBLEMgr.closeBluetooth((str: string)=>{let content: string = "";if (str == 'STATE_OFF') {content = "蓝牙已关闭";}else{content = "关闭错误:" + str;}promptAction.showToast({message: content});});}else{this.toSysSettingPage();}}onClickStartAdv = ()=>{this.mBLEMgr.startAdvertising((advState: string)=>{let content: string = "";if(advState == "STARTED"){content = "广播已开启";}else{content = "广播错误:" + advState;}promptAction.showToast({message: content});});}onClickCloseAdv = ()=>{this.mBLEMgr.stopAdvertising((str: string)=>{promptAction.showToast({message: str});});}onClickStartScan = ()=>{this.mBLEMgr.startScan();}onClickCloseScan = ()=>{this.mBLEMgr.stopScan();}onClickStartServer = ()=>{this.mBLEMgr.registerServer((res: string)=>{promptAction.showToast({message: res});});}onClickCloseServer = ()=>{this.mBLEMgr.unRegisterServer((res: string)=>{promptAction.showToast({message: res});});} LineView(){Line().width("100%").height(px2vp(2)).backgroundColor(Color.Black).margin({top: px2vp(100)})}build() {Column() {Column(){Text(this.isOpenBluetooth ? "蓝牙状态: 已开启" : "蓝牙状态: 已关闭")Text("蓝牙设备名:" + this.mBLEMgr.getCurrentDeviceName())}Text("开启蓝牙").attributeModifier(this.txtModifier).onClick(this.onClickStart)Text("关闭蓝牙").attributeModifier(this.txtModifier).onClick(this.onClickClose)this.LineView()Text("启动服务").attributeModifier(this.txtModifier).onClick(this.onClickStartServer)Text("关闭服务").attributeModifier(this.txtModifier).onClick(this.onClickCloseServer)Text("开启广播").attributeModifier(this.txtModifier).onClick(this.onClickStartAdv)Text("关闭广播").attributeModifier(this.txtModifier).onClick(this.onClickCloseAdv)this.LineView()Text("开启扫描").attributeModifier(this.txtModifier).onClick(this.onClickStartScan)Text("关闭扫描").attributeModifier(this.txtModifier).onClick(this.onClickCloseScan)}.height('100%').width('100%')}
}

BLEMgr.ets

import bleAdvertisingManager from "./BleAdvertisingManager";
import bleScanManager from "./BleScanManager";
import { access } from '@kit.ConnectivityKit';
import gattServerManager from "./GattServerManager";
import { connection } from '@kit.ConnectivityKit';
import { BusinessError } from "@kit.BasicServicesKit";const TAG: string = "BLEMgr";export class BLEMgr {public getBluetoothState(): boolean {let state = access.getState();return this.getStateName(state) == "STATE_ON" ? true : false;}/*** 当前设备蓝牙设备名称*/public getCurrentDeviceName(){let localName: string = "";try {localName = connection.getLocalName();console.info(TAG, 'getCurrentDeviceName localName: ' + localName);} catch (err) {console.error(TAG, 'getCurrentDeviceName errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}return localName;}/*** 蓝牙广播状态* @param state* @returns*/public getAdvState(state: number){switch (state) {case 1:// 首次启动广播后的状态。return 'STARTED';case 2:// 临时启动广播后的状态。return 'ENABLED';case 3:// 临时停止广播后的状态。return 'DISABLED';case 4:// 完全停止广播后的状态。return 'STOPPED';default:return 'unknown status';}}/*** 蓝牙开启状态* @param state* @returns*/private getStateName(state: number): string {switch (state) {case 0:// 蓝牙已关闭。return 'STATE_OFF'; ;case 1:// 蓝牙正在打开。return 'STATE_TURNING_ON';case 2:// 蓝牙已打开。return 'STATE_ON';case 3:// 蓝牙正在关闭。return 'STATE_TURNING_OFF';case 4:// 蓝牙正在打开LE-only模式。return 'STATE_BLE_TURNING_ON';case 5:// 蓝牙正处于LE-only模式。return 'STATE_BLE_ON';case 6:// 蓝牙正在关闭LE-only模式。return 'STATE_BLE_TURNING_OFF';default:return 'unknown status';}}/*** 开启蓝牙*/public startBluetooth(callback: (str: string)=> void){try {access.enableBluetooth();} catch (err) {let errStr: string = JSON.stringify(err);console.info(TAG, 'startBluetooth enableBluetooth err: ' + errStr);callback(errStr);}access.on('stateChange', (data) => {let btStateMessage = this.getStateName(data);callback(btStateMessage);if (btStateMessage == 'STATE_ON') {access.off('stateChange');}console.info('bluetooth statues: ' + btStateMessage);});}/*** 关闭蓝牙*/public closeBluetooth(callback: (str: string)=> void){access.disableBluetooth();access.on('stateChange', (data) => {let btStateMessage = this.getStateName(data);callback(btStateMessage);if (btStateMessage == 'STATE_OFF') {access.off('stateChange');}console.info("bluetooth statues: " + btStateMessage);})}/*** 创建GATT服务器,注册服务*/public registerServer(callBack: (str: string)=> void){gattServerManager.registerServer(callBack);}/*** 删除服务,关闭GATT服务器*/public unRegisterServer(callBack: (str: string)=> void){gattServerManager.unRegisterServer(callBack);}/*** 开启广播*/public async startAdvertising(callBack: (state: string)=> void) {await bleAdvertisingManager.startAdvertising((state: number)=>{let advState: string = this.getAdvState(state);callBack(advState);});}/*** 关闭广播*/public async stopAdvertising(callBack: (str: string)=> void) {await bleAdvertisingManager.stopAdvertising(callBack);}/*** 开始扫描*/public startScan() {bleScanManager.startScan();}/*** 关闭扫描*/public stopScan() {bleScanManager.stopScan();}}

GattServermanager.ets

import { ble } from '@kit.ConnectivityKit';
import { constant } from '@kit.ConnectivityKit';
import { BusinessError } from '@kit.BasicServicesKit';const TAG: string = 'GattServerManager';export class GattServerManager {gattServer: ble.GattServer | undefined = undefined;connectState: ble.ProfileConnectionState = constant.ProfileConnectionState.STATE_DISCONNECTED;myServiceUuid: string = '00001810-0000-1000-8000-00805F9B34FB';myCharacteristicUuid: string = '00001820-0000-1000-8000-00805F9B34FB';myFirstDescriptorUuid: string = '00002902-0000-1000-8000-00805F9B34FB'; // 2902一般用于notification或者indicationmySecondDescriptorUuid: string = '00002903-0000-1000-8000-00805F9B34FB';// 构造BLEDescriptorprivate initDescriptor(des: string, value: ArrayBuffer): ble.BLEDescriptor {let descriptor: ble.BLEDescriptor = {serviceUuid: this.myServiceUuid,characteristicUuid: this.myCharacteristicUuid,descriptorUuid: des,descriptorValue: value};return descriptor;}// 构造BLECharacteristicprivate initCharacteristic(): ble.BLECharacteristic {let descriptors: Array<ble.BLEDescriptor> = [];let descBuffer = new ArrayBuffer(2);let descValue = new Uint8Array(descBuffer);descValue[0] = 31;descValue[1] = 32;descriptors[0] = this.initDescriptor(this.myFirstDescriptorUuid, new ArrayBuffer(2));descriptors[1] = this.initDescriptor(this.mySecondDescriptorUuid, descBuffer);let charBuffer = new ArrayBuffer(2);let charValue = new Uint8Array(charBuffer);charValue[0] = 21;charValue[1] = 22;let characteristic: ble.BLECharacteristic = {serviceUuid: this.myServiceUuid,characteristicUuid: this.myCharacteristicUuid,characteristicValue: charBuffer,descriptors: descriptors};return characteristic;}// 1. 订阅连接状态变化事件public onGattServerStateChange() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}try {this.gattServer.on('connectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => {let state = '';switch (stateInfo.state) {case 0:state = 'DISCONNECTED';break;case 1:state = 'CONNECTING';break;case 2:state = 'CONNECTED';break;case 3:state = 'DISCONNECTING';break;default:state = 'undefined';break;}console.info(TAG, 'onGattServerStateChange: device=' + stateInfo.deviceId + ', state=' + state);});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 2. server端注册服务时调用public registerServer(callBack: (str: string)=> void) {let characteristics: Array<ble.BLECharacteristic> = [];let characteristic = this.initCharacteristic();characteristics.push(characteristic);let gattService: ble.GattService = {serviceUuid: this.myServiceUuid,isPrimary: true,characteristics: characteristics};console.info(TAG, 'registerServer ' + this.myServiceUuid);try {this.gattServer = ble.createGattServer(); // 2.1 构造gattServer,后续的交互都需要使用该实例this.onGattServerStateChange(); // 2.2 订阅连接状态this.gattServer.addService(gattService);callBack("服务成功");} catch (err) {callBack("服务失败:" + JSON.stringify(err));console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 3. 订阅来自gattClient的读取特征值请求时调用public onCharacteristicRead() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onCharacteristicRead');try {this.gattServer.on('characteristicRead', (charReq: ble.CharacteristicReadRequest) => {let deviceId: string = charReq.deviceId;let transId: number = charReq.transId;let offset: number = charReq.offset;console.info(TAG, 'receive characteristicRead');let rspBuffer = new ArrayBuffer(2);let rspValue = new Uint8Array(rspBuffer);rspValue[0] = 21;rspValue[1] = 22;let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer?.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 4. 订阅来自gattClient的写入特征值请求时调用public onCharacteristicWrite() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onCharacteristicWrite');try {this.gattServer.on('characteristicWrite', (charReq: ble.CharacteristicWriteRequest) => {let deviceId: string = charReq.deviceId;let transId: number = charReq.transId;let offset: number = charReq.offset;console.info(TAG, 'receive characteristicWrite: needRsp=' + charReq.needRsp);if (!charReq.needRsp) {return;}let rspBuffer = new ArrayBuffer(0);let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer?.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 5. 订阅来自gattClient的读取描述符请求时调用public onDescriptorRead() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onDescriptorRead');try {this.gattServer.on('descriptorRead', (desReq: ble.DescriptorReadRequest) => {let deviceId: string = desReq.deviceId;let transId: number = desReq.transId;let offset: number = desReq.offset;console.info(TAG, 'receive descriptorRead');let rspBuffer = new ArrayBuffer(2);let rspValue = new Uint8Array(rspBuffer);rspValue[0] = 31;rspValue[1] = 32;let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer?.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 6. 订阅来自gattClient的写入描述符请求时调用public onDescriptorWrite() {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'onDescriptorWrite');try {this.gattServer.on('descriptorWrite', (desReq: ble.DescriptorWriteRequest) => {let deviceId: string = desReq.deviceId;let transId: number = desReq.transId;let offset: number = desReq.offset;console.info(TAG, 'receive descriptorWrite: needRsp=' + desReq.needRsp);if (!desReq.needRsp) {return;}let rspBuffer = new ArrayBuffer(0);let serverResponse: ble.ServerResponse = {deviceId: deviceId,transId: transId,status: 0, // 0表示成功offset: offset,value: rspBuffer};try {this.gattServer?.sendResponse(serverResponse);} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}});} catch (err) {console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}// 7. server端删除服务,不再使用时调用public unRegisterServer(callBack: (str: string)=> void) {if (!this.gattServer) {console.error(TAG, 'no gattServer');return;}console.info(TAG, 'unRegisterServer ' + this.myServiceUuid);try {this.gattServer.removeService(this.myServiceUuid); // 7.1 删除服务callBack("关闭服务");this.gattServer.off('connectionStateChange', (stateInfo: ble.BLEConnectionChangeState) => { // 7.2 取消订阅连接状态});this.gattServer.close() // 7.3 如果不再使用此gattServer,则需要close} catch (err) {callBack("关闭失败:" + JSON.stringify(err));console.error(TAG, 'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);}}
}let gattServerManager = new GattServerManager();
export default gattServerManager as GattServerManager;

其余GattClientManager BleScanManager BleAdvertisingManager 类可以参考官方文档。

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

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

相关文章

基于STM32的数字温度计教学

引言 数字温度计是许多电子项目中的常见应用&#xff0c;它可以实时监测周围环境的温度并显示。利用STM32微控制器实现数字温度计&#xff0c;可以帮助我们了解如何使用传感器进行模拟信号转换及数据传输。本教程将指导您使用STM32和DS18B20数字温度传感器实现一个简单的数字温…

go mod 依赖管理

什么是go mod go mod 是从 Go 1.11 版本开始引入的。Go Modules 是 Go 语言官方提供的一个版本管理工具&#xff0c;旨在解决依赖管理和版本控制的问题。从 Go 1.11 开始&#xff0c;Go Modules 被作为实验性功能引入&#xff0c;到了 Go 1.13 版本&#xff0c;Go Modules 成为…

洛谷解题日记||基础篇2

题目链接 0 剪刀&#xff0c;1 石头&#xff0c;2 布&#xff0c;3 蜥蜴人&#xff0c;4 斯波克我们可以根据题意&#xff0c;构建一个二维矩阵 result[5][5] 来表示每一种出拳的胜负情况。 #include <iostream> #include <vector> using namespace std;int m…

年会必备的抽奖小程序!

感觉一年一度的年会快来了&#xff0c;准备个小的抽奖小程序蛮陶冶情操的&#xff01; 正好今天有空整了一个&#xff0c;简单&#xff0c;简洁&#xff01;&#xff0c;大家也可以玩起来&#xff01; 技术栈&#xff1a; Flask 试玩的链接在这里,有意思的话也可以部署到自己的…

sqoop Oracle 导入到hive 日期时间消失

sqoop脚本&#xff1a; sqoop import -D mapred.job.queue.namehighway \ -D mapreduce.map.memory.mb4096 \ -D mapreduce.map.java.opts-Xmx3072m \ --connect "jdbc:oracle:thin://localhost:61521/LZY2" \ --username LZSHARE \ --password 123456 \ --query &q…

20241108通过iperf3确认中科创达的高通CM6125的WIFI的网速【失败】

20241108通过iperf3确认中科创达的高通CM6125的WIFI的网速【失败】 2024/11/8 15:43 由于以太网不能用&#xff0c;那就测试一下WIFI&#xff0c;iperf3链接/测试异常。 一般认为可能的原因有&#xff1a; 1、CM6125开发板的WIFI不带天线&#xff0c;影响性能。 2、CM6125的And…

Vue项目中点击按钮后浏览器屏幕变黑,再次点击恢复的解决方法

情况说明 点击按钮后出现以下情况&#xff0c;浏览器屏幕变暗&#xff0c;再次点击则恢复正常。 解决方法&#xff1a; 找到你的全局样式文件&#xff0c;我的是gloable.css&#xff0c;添加以下代码&#xff1a; .v-modal {display: none; }重启服务器即可解决。

05栈和队列/代码随想录

六、栈和队列 6.1数据结构的应用 用栈实现队列 力扣232 很简单&#xff0c;添加的时候正常加在弹入栈&#xff0c;删除的时候把元素放到弹出栈&#xff0c;直接调用java集合实现的Stack class MyQueue {Stack<Integer> stackIn;Stack<Integer> stackout;public M…

51c大模型~合集18

我自己的原文哦~ https://blog.51cto.com/whaosoft/11621494 #SpatialBot 空间大模型&#xff1a;上交、斯坦福、智源、北大、牛津、东大联合推出&#xff01; 大模型走向空间智能、具身智能之路&#xff01; 智源&#xff0c;斯坦福&#xff0c;北大&#xff0c;牛津&…

国外白帽故事 | 攻破大学数据库系统,暴露数千学生记录

引言 在这篇文章中&#xff0c;我将分享我是如何攻破一个大型大学解决方案门户服务器的&#xff0c;这个服务器服务于许多大学客户&#xff0c;并且涉及数千名学生的数据。 目标 这是一个由印度许多大学和学院使用的门户网站&#xff0c;用于管理学生记录、成绩单、出勤记录…

苍穹外卖05-Redis相关知识点

目录 什么是Redis&#xff1f; redis中的一些常用指令 value的5种常用数据类型 各种数据类型的特点 Redis中数据操作的常用命令 字符串类型常用命令&#xff1a; 哈希类型常用命令 列表操作命令 集合操作命令 有序集合操作命令 通用命令 在java中操作Redis 环境…

【MySQL】数据的增删查改

文章目录 1. 插入数据(Create)1.1 全列插入1.2 指定列插入1.3 多行数据插入1.4 插入否则更新1.5 替换 2. 读取数据(Retrieve)2.1 select列2.2 where条件2.3 结果排序2.4 筛选分页结果 3. 修改数据(Update)4. 删除数据(delete)4.1 删除数据4.2 截断表 5. 插入查询的结果6. 分组与…

【案例分享】借助 iSpring,创造客户真正欣赏的专业在线培训体验

Safety Bee Training是一家领先的认证在线学习提供商&#xff0c;专门提供职业健康、安全和环境项目。它也是中东和亚洲唯一一家提供经 NASP 等国际认证机构认可的课程的培训提供商。它已经培训了超过 28,000 名学习者&#xff0c;并且正在不断扩大其课程范围&#xff0c;以提供…

【连续多届检索,ACM出版】第四届大数据、人工智能与风险管理国际学术会议 (ICBAR 2024,11月15-17)--冬季主会场

第四届大数据、人工智能与风险管理国际学术会议 (ICBAR 2024)--冬季主会场 2024 4th International Conference on Big Data, Artificial Intelligence and Risk Management 会议官网&#xff1a;www.icbar.net 2024 4th International Conference on Big Data, Artificial I…

界面设计软件:10款设计师必备工具

UI界面设计软件是设计师们不可或缺的工具&#xff0c;它们提供了一系列功能和直观的操作界面&#xff0c;助力设计师迅速打造精美且用户友好的界面。面对众多UI设计软件&#xff0c;有的提供预设模板和图标库&#xff0c;有的更侧重于原型和交互设计。如何选择最适合自己的UI设…

TCP(上):成熟可靠的传输层协议

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持! TCP&#xff08;传输控制协议&#xff09;是位于传输层的通信协议&#xff0c;是一种面向连接的、可靠的、基于字节流的传输层通信协议。主要负责在不可靠的网络环境中提供可靠的端到端字节流传输服务。TCP是…

如何在Windows中检查是否安装了GPU

文章目录 1. 系统设备管理器1.1 打开设备管理器1.2 查找显示适配器 2. 命令行工具2.1 打开命令提示符2.2 执行WMIC命令 3. DirectX诊断工具3.1 运行DirectX诊断工具3.2 查看显示信息 在Windows操作系统中&#xff0c;了解您的电脑是否配备了图形处理单元&#xff08;GPU&#x…

网络技术----wireshark抓包出现1500以上的大包原因分析

网络技术----wireshark抓包出现1500以上的大包原因分析 背景描述原因分析TSO&#xff08;TCP segment offload&#xff0c;TSO&#xff09;linux中关闭/开启TSO功能&#xff1a;其他类似TSO的机制 wireshark抓包来源 背景描述 我们在使用抓包工具的过程中&#xff0c;经常发现…

3.3 软件需求:面对对象分析模型

面对对象分析模型 1、对象2、面对对象的软件开发模型3、用例图建模基础3.1 用例图基本符号参与者用例系统执行关联 3.2 用例建模过程3.3 用例图初步3.4 用例图进阶关联Association泛化Inheritance包含Include扩展Extend示例 1、对象 在现实世界中有意义的&#xff0c;与所要解…

跑批为什么这么难

业务系统产生的明细数据通常要经过加工处理&#xff0c;按照一定逻辑计算成需要的结果&#xff0c;用以支持企业的经营活动。这类数据加工任务一般会有很多个&#xff0c;需要批量完成计算&#xff0c;在银行和保险行业常常被称为跑批&#xff0c;其它像石油、电力等行业也经常…