Android Ble藍牙App(五)數據操作

Ble藍牙App(五)數據操作

  • 前言
  • 正文
    • 一、操作內容處理
    • 二、讀取數據
      • ① 概念
      • ② 實操
    • 三、寫入數據
      • ① 概念
      • ② 實操
    • 四、打開通知
      • 一、概念
      • 二、實操
      • 三、收到數據
    • 五、源碼

前言

??關于低功耗藍牙的服務、特性、屬性、描述符都已經講清楚了,而下面就是使用這些知識進行數據的讀取、寫入、通知等操作。

正文

??首先要做的就是根據操作內容進行相應的處理,目前常見的操作有Read、Write、Write no response、Notify和Indicate。

一、操作內容處理

??首先要修改MainActivity中的onPropertyOperate()函數,

    override fun onPropertyOperate(characteristic: BluetoothGattCharacteristic, operateName: String) {if (!bleCore.isConnected()) showMsg("設備已斷開連接")when (operateName) {READ -> {}WRITE, WRITE_NO_RESPONSE -> {}NOTIFY, INDICATE -> {}BROADCAST, AUTHENTICATED_SIGNED_WRITES, EXTENDED_PROPERTIES -> showMsg(operateName)}}

這里著重看剛才提到的5個操作,在操作之前我們最好判斷一下當前是否處于連接中,在BleCore中增加isConnected()函數,代碼如下所示:

	fun isConnected() = mIsConnected

二、讀取數據

① 概念

??在BLE(Bluetooth Low Energy)通信中,Ble Read(讀操作)是一種用于從BLE服務器設備讀取數據的操作。

當一個BLE設備(稱為客戶端)需要獲取另一個BLE設備(稱為服務器)上的數據時,可以使用Ble Read操作。客戶端向服務器發送讀取請求,并等待服務器返回所請求的數據。

Ble Read操作具有以下特點:

  1. 請求-回復模式:Ble Read操作是一種請求-回復模式的操作,客戶端向服務器發送讀取請求,服務器則回復所請求的數據。這種模式保證了數據傳輸的可靠性和順序性。

  2. 單次數據傳輸:Ble Read操作一次只能讀取一個數據值或一個數據塊。如果需要讀取多個數據值,客戶端需要連續發送多個讀取請求。

  3. 數據的訪問權限:Ble Read操作只能讀取具有權限允許的數據。服務器可以設定數據的訪問權限,例如只允許讀取、只允許寫入、或者讀寫均允許。

??需要注意的是,Read操作可能會引入一定的延遲,因為客戶端需要等待服務器的響應。此外,Read操作的成功取決于服務器是否支持讀取請求,并且客戶端是否具有讀取權限。

② 實操

??當特性擁有Read的屬性時,我們就可以讀取特性的value,在的BleCoreBleGattCallback中,重寫onCharacteristicRead()函數,代碼如下所示:

        /*** 讀取特性回調 Android 13及以上使用*/override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {if (status != BluetoothGatt.GATT_SUCCESS) returndeviceInfo("讀取特性值(Android 13及以上):${BleUtils.bytesToHex(value, true)}")}/*** 讀取特性回調 Android 12及以下使用*/@Deprecated("Deprecated in Java")override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {if (status != BluetoothGatt.GATT_SUCCESS) returndeviceInfo("讀取特性值(Android 12及以下):${BleUtils.bytesToHex(characteristic.value, true)}")}

??bytesToHex()是將byte[]轉成Hex的函數,還有hexToBytes的函數,我們在BleUtils中增加這兩個函數,代碼如下所示:

    /*** byte[] to hex* @param isAdd 是否添加 0x 頭*/fun bytesToHex(byteArray: ByteArray, isAdd: Boolean = false): String {val hexChars = "0123456789ABCDEF"val hexString = StringBuilder()for (byte in byteArray) {val value = byte.toInt() and 0xFFval firstIndex = value shr 4 and 0x0Fval secondIndex = value and 0x0FhexString.append(hexChars[firstIndex])hexString.append(hexChars[secondIndex])}return (if (isAdd) "0x" else "" ) + hexString.toString()}/*** hex to byte[]*/fun hexToBytes(hexString: String): ByteArray {val cleanHexString = hexString.replace("\\s".toRegex(), "")val byteArray = ByteArray(cleanHexString.length / 2)for (i in byteArray.indices) {val index = i * 2val byteString = cleanHexString.substring(index, index + 2)val byteValue = byteString.toInt(16).toByte()byteArray[i] = byteValue}return byteArray}

??讀取特性之后如果狀態正常,我們就顯示一下讀取的內容,當我們調用Gatt的readCharacteristic()函數時就會觸發這個回調。下面在BleCore中增加readCharacteristic()函數,代碼如下所示:

    fun readCharacteristic(characteristic: BluetoothGattCharacteristic) {deviceInfo("讀取特性: ${BleUtils.getShortUUID(characteristic.uuid)}")mGatt?.readCharacteristic(characteristic)}

然后修改onPropertyOperate()函數,代碼如下所示:

    override fun onPropertyOperate(characteristic: BluetoothGattCharacteristic, operateName: String) {when (operateName) {READ -> bleCore.readCharacteristic(characteristic)...}}

下面我們運行一下:

在這里插入圖片描述

三、寫入數據

??讀取數據寫好了,下面我們來看寫入數據,寫入數據要看寫入的方式,有Write和Wirte No Response,我們先了解這兩種方式的區別:

① 概念

??在BLE通信中,有兩種常用的寫操作方式:Ble Write(帶回復的寫操作)和Write No Response(無回復的寫操作)。

  1. Ble Write(帶回復的寫操作):當一個BLE設備(稱為客戶端)想要向另一個BLE設備(稱為服務器)發送數據時,可以使用Ble Write操作。客戶端向服務器發送數據并等待服務器發送確認回復(Acknowledgment)來表示數據已經被成功接收。這種寫操作是一種可靠的方式,確保數據傳輸的可靠性。

  2. Write No Response(無回復的寫操作):在某些情況下,客戶端發送的數據并不需要服務器的確認回復,或者在時間上要求更加緊湊的傳輸。這時可以使用Write No Response操作。客戶端向服務器發送數據后,并不會等待服務器的確認回復。這種寫操作通常用于實時傳輸等不需要確認的數據,以減少通信延遲和增加通信吞吐量。

??需要注意的是,Write No Response操作在數據傳輸過程中不提供任何保障機制,例如數據的可靠性、順序性或冪等性等。因此,使用Write No Response操作時需要確保應用場景的需求和通信的可靠性。

② 實操

??寫入數據需要有一個輸入框,因此我就寫了一個彈窗來進行操作,首先寫彈窗布局,在layout下新建一個dialog_write_data.xml,代碼如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/white"><com.google.android.material.appbar.MaterialToolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:title="寫入數據" /><com.google.android.material.textfield.TextInputLayoutandroid:id="@+id/data_layout"style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginStart="16dp"android:layout_marginEnd="16dp"app:boxStrokeColor="@color/black"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/toolbar"app:prefixText="0x"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/et_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="HEX數據"android:inputType="text|textCapCharacters"android:lines="1"android:singleLine="true" /></com.google.android.material.textfield.TextInputLayout><Buttonandroid:id="@+id/btn_negative"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="18dp"android:layout_weight="1"android:text="取消"app:layout_constraintEnd_toStartOf="@+id/btn_positive"app:layout_constraintTop_toTopOf="@+id/btn_positive" /><Buttonandroid:id="@+id/btn_positive"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="16dp"android:layout_marginBottom="16dp"android:layout_weight="1"android:text="發送"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="@+id/data_layout"app:layout_constraintTop_toBottomOf="@+id/data_layout" /></androidx.constraintlayout.widget.ConstraintLayout>

??布局內容比較簡單,只需要一個輸入框兩個按鈕即可,下面我們在MainActivity中寫一個函數來加載這個布局xml顯示彈窗,代碼如下所示:

    /*** 顯示寫入數據彈窗*/private fun showWriteDataDialog(characteristic: BluetoothGattCharacteristic, operateName: String) {val dialog = BottomSheetDialog(this, R.style.BottomSheetDialogStyle)val writeDataBinding = DialogWriteDataBinding.inflate(layoutInflater)writeDataBinding.toolbar.title = if (operateName == WRITE) "寫入數據" else "寫入無需響應數據"writeDataBinding.btnPositive.setOnClickListener {val inputData = writeDataBinding.etData.text.toString()if (inputData.isEmpty()) {writeDataBinding.dataLayout.error = "請輸入數據"return@setOnClickListener}if (!BleUtils.isHexFormat(inputData)) {writeDataBinding.dataLayout.error = "請輸入有效數據"return@setOnClickListener}bleCore.writeCharacteristic(characteristic, inputData, operateName)dialog.dismiss()}writeDataBinding.btnNegative.setOnClickListener {dialog.dismiss()}dialog.setContentView(writeDataBinding.root)dialog.show()}

??在彈窗中,根據傳入的操作名判斷要以什么方式寫入數據,同時對寫入的數據進行了格式校驗,在BleUtils中增加函數,代碼如下所示:

fun isHexFormat(str: String) = Regex("^([\\dA-Fa-f]{2})+$").matches(str)

??當檢查數據無誤之后我們就可以寫入數據了,調用bleCore.writeCharacteristic(characteristic, inputData, operateName),在BleCore中增加這個函數,代碼如下所示:

    /*** 寫入特性* @param characteristic 特性* @param data Hex數據* @param operateName 操作名,決定寫入的是 Write 還是 Write No Response*/fun writeCharacteristic(characteristic: BluetoothGattCharacteristic, data: String, operateName: String) {deviceInfo("寫入特性:${BleUtils.getShortUUID(characteristic.uuid)},value:0x$data")//寫入類型val writeType = if (operateName == BleConstant.WRITE) BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT else BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE//寫入數據val byteArray = BleUtils.hexToBytes(data)//根據Android版本進行不同的寫入方式 Android 13及以上和以下不同val executionResult = if (isAndroid13()) {mGatt?.writeCharacteristic(characteristic, byteArray, writeType) == BluetoothStatusCodes.SUCCESS} else {characteristic.writeType = writeTypecharacteristic.value = byteArraymGatt?.writeCharacteristic(characteristic)}//執行寫入動作成功不代表寫入數據成功,執行寫入動作失敗,寫入數據一定失敗deviceInfo(if (executionResult == true)  "執行寫入動作成功" else "執行寫入動作失敗")}

??這個函數相對的內容多一些,首先是根據操作名得到寫入的類型,然后獲取寫入的數據,再根據Android的版本去寫入數據,最終調用Gatt的writeCharacteristic()函數進行寫入,寫入屬于一個執行動作,有失敗的可能性,可以根據返回值進行判斷,Android13以前返回的是Boolean,Android13及以上返回的是Int,這里要注意一下。執行之后如果成功了,則會觸發GattCallback的onCharacteristicWrite()回調,下面在BleGattCallback中重寫這個函數,代碼如下所示:

        override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {if (status != BluetoothGatt.GATT_SUCCESS) returnif (BleUtils.isAndroid13()) {gatt.readCharacteristic(characteristic)} else {deviceInfo("寫入成功:${BleUtils.bytesToHex(characteristic.value)}")}}

??這個函數中,如果是Android 13及以上版本,寫入回調中的value是null,需要通過readCharacteristic()函數去獲取寫入的值,但是要確保這個特性有Read屬性,否則讀取不了,這個地方也是我覺得不合理得地方,也有可能是我沒找到對應得方式吧。最后我們修改MainActivity中的onPropertyOperate()函數中的代碼,如下所示:

    override fun onPropertyOperate(characteristic: BluetoothGattCharacteristic, operateName: String) {when (operateName) {WRITE, WRITE_NO_RESPONSE -> showWriteDataDialog(characteristic, operateName)...}}

??最后我們再修復一個bug,沒錯,前面寫的時候這個bug忽略掉了,那就是在CharacteristicAdapteronBindViewHolder()函數中,之前在這里對屬性的點擊進行了回調,當時是傳進去一個特性,和一個操作名稱,如圖所示
在這里插入圖片描述
??這里通過position獲取到特性,而這里的position是屬性適配器,而我們要的是特性適配器的position,這樣做的問題就在于使用的時候如果只有一個屬性的話,那么無論有幾個特性,position都是0,也是在調試中發現的這個問題,改完之后代碼如下所示:

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {val characteristic = characteristics[position]val characteristicName = BleUtils.getCharacteristicsName(characteristic.uuid)holder.binding.tvCharacterName.text = characteristicNameholder.binding.tvCharacterUuid.text = if (characteristicName != UNKNOWN_CHARACTERISTICS) BleUtils.getShortUUID(characteristic.uuid) else characteristic.uuid.toString()//加載特性下的屬性holder.binding.rvProperty.apply {layoutManager = LinearLayoutManager(context).apply { orientation = LinearLayoutManager.HORIZONTAL }val properties: List<String> = BleUtils.getProperties(characteristic.properties)adapter = PropertyAdapter(properties, object : OnItemClickListener {//點擊屬性override fun onItemClick(view: View?, position: Int) { callback.onPropertyOperate(characteristic, properties[position]) }})}//加載特性下的描述if (characteristic.descriptors.isEmpty()) {holder.binding.layDescriptors.visibility = View.GONEreturn}holder.binding.rvDescriptor.apply {layoutManager = LinearLayoutManager(context)adapter = DescriptorAdapter(characteristic.descriptors)}}

為了方便查看動作,我們在修改一下BleCore中的deviceInfo()函數代碼,加一個日志打印,代碼如下所示:

        private fun deviceInfo(info: String) {Log.d(TAG, "deviceInfo: $info")mBleCallback?.deviceInfo(info)}

下面運行一下:

在這里插入圖片描述

日志如下所示:

在這里插入圖片描述

四、打開通知

??實際上打開通知的意義就是能夠收到藍牙設備返回的數據,先了解以下相關的概念知識。

一、概念

??Ble Enable Notify是指在藍牙低功耗(BLE)通信中使能通知功能的操作。當設備之間建立了藍牙連接后,設備可以通過特征(Characteristic)來交換數據。通知(Notification)是一種特征的屬性,允許一個設備向另一個設備發送數據,而不需要另一個設備主動請求。

??當一個設備使能了通知功能(Enable Notify),它就可以向另一個設備發送通知,另一個設備只需要注冊監聽這個特征的通知即可接收到數據。這樣可以實現數據的異步傳輸,一旦數據發生變化,發送方會自動發出通知,接收方就可以及時獲取到最新的數據。在BLE開發中,通常需要通過操作特征的屬性來使能或禁用通知功能。

二、實操

??下面我們來實際操作一下,首先在BleCore中增加一個函數,代碼如下所示:

    /*** 開啟或者關閉通知* @param characteristic 特性* @param descriptorUuid 描述UUID* @param operateName 操作名, 決定通過那種方式開啟通知*/fun notifyEnable(characteristic: BluetoothGattCharacteristic, descriptorUuid: UUID, operateName: String) {//設置特性通知,這一點很重要if (mGatt?.setCharacteristicNotification(characteristic,true) == false) return//描述val descriptor = characteristic.getDescriptor(descriptorUuid)//寫入描述值val value = if (!mIsEnabled) {if (operateName == BleConstant.INDICATE) BluetoothGattDescriptor.ENABLE_INDICATION_VALUE else BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE} else {BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE}val executionResult = if (isAndroid13()) {mGatt?.writeDescriptor(descriptor, value) == BluetoothStatusCodes.SUCCESS} else {descriptor.value = valuemGatt?.writeDescriptor(descriptor)}deviceInfo((if (executionResult == true)  "執行啟用動作成功" else "執行啟用動作失敗") + ",value: ${BleUtils.bytesToHex(value, true)}" )}

??因為當前的項目環境是基于Android13,所在在藍牙的一些API處理上,我們都要考慮兼容的問題,我覺得奇怪的是,為什么不在Android12的版本中順便加上去這些改動的API,也不重要,開發者就是這個命,這里的代碼實際上比較簡單,就是根據操作名進行enable的方式,通過一個變量mIsEnabled來決定你是打開通知還是關閉通知,這個變量我們定義在companion object中,代碼如下所示:

    companion object {.../*** 是否開啟通知*/private var mIsEnabled = false}

??調用writeDescriptor()會觸發描述符寫入回調,在BleGattCallback中增加這個回調,代碼如下所示:

        /*** 描述符寫入回調*/override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {if (status != BluetoothGatt.GATT_SUCCESS) returnif (BleUtils.isAndroid13()) {gatt.readDescriptor(descriptor) //讀取描述符} else {mIsEnabled = !descriptor.value.contentEquals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)deviceInfo("寫入描述符成功:${BleUtils.bytesToHex(descriptor.value, true)}")}}

??在回調中,處理mIsEnabled的賦值,因為在Android 13中沒有辦法直接獲取描述符結果,而是需要通過readDescriptor()函數獲取,使用這個函數,則會觸發另一個回調函數,同樣是在BleGattCallback中增加這個回調,代碼如下所示:

        /*** 讀取描述符回調 Android 13及以上使用*/override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {if (status != BluetoothGatt.GATT_SUCCESS) returnmIsEnabled = !value.contentEquals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)deviceInfo("讀取描述符成功(Android 13及以上使用):${BleUtils.bytesToHex(value, true)}")}/*** 讀取描述符回調 Android 12及以上下使用*/@Deprecated("Deprecated in Java")override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {if (status != BluetoothGatt.GATT_SUCCESS) returnmIsEnabled = !descriptor.value.contentEquals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)deviceInfo("讀取描述符成功(Android 12及以下使用):${BleUtils.bytesToHex(descriptor.value, true)}")}

??關于mIsEnabled的參數我們還需要修改一下一個地方,那就是在連接設備之后如果發現mIsEnabled 為true,我們改成false。

    fun connect(device: BluetoothDevice) {deviceInfo("連接中...")if (mIsEnabled) mIsEnabled = false...}

然后我們再修改一下MainActivity中的onPropertyOperate()函數,代碼如下所示:

    /*** 屬性操作*/override fun onPropertyOperate(characteristic: BluetoothGattCharacteristic, operateName: String) {if (!bleCore.isConnected()) showMsg("設備已斷開連接")Log.d("TAG", "onPropertyOperate: ${characteristic.uuid}")when (operateName) {READ -> bleCore.readCharacteristic(characteristic)WRITE, WRITE_NO_RESPONSE -> showWriteDataDialog(characteristic, operateName)NOTIFY, INDICATE -> bleCore.notifyEnable(characteristic, characteristic.descriptors[0].uuid, operateName)BROADCAST, AUTHENTICATED_SIGNED_WRITES, EXTENDED_PROPERTIES -> showMsg(operateName)}}

那么到現在為止我們就寫好了基本的操作方式。

三、收到數據

??下面我們寫一下接收通知的回調,同樣是在BleGattCallback中增加這個回調,代碼如下所示:

        /*** 收到數據回調 Android 13及以上使用*/override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {deviceInfo("收到特性值(Android 13及以上):${BleUtils.getShortUUID(characteristic.uuid)}${BleUtils.bytesToHex(value, true)}")}/*** 收到數據回調 Android 12及以下使用*/@Deprecated("Deprecated in Java")override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {deviceInfo("收到特性值(Android 12及以下):${BleUtils.getShortUUID(characteristic.uuid)}${BleUtils.bytesToHex(characteristic.value, true)}")}

??下面我們運行一下,這里你要以自己的實際設備為準,比如我用的這個設備,包括數據的交互都是廠商自定義的,下面我先開啟Notify然后寫入數據,再看是否有數據返回。

在這里插入圖片描述

??我們再看一下控制臺日志

在這里插入圖片描述

??可以看到在執行寫入動作成功之后,就收到了設備所回復的特征值數據,然后再是收到寫入成功的日志打印。

五、源碼

如果對你有所幫助的話,不妨 StarFork,山高水長,后會有期~

源碼地址:GoodBle

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/37040.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/37040.shtml
英文地址,請注明出處:http://en.pswp.cn/news/37040.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電腦系統重裝日記

重裝原因 電腦C盤幾乎爆炸故重裝系統一清二白 此片原因 記錄重裝過程&#xff0c;強調一些要注意的點&#xff0c;以防日后重裝。 重裝過程 1.清空電腦文件后重啟&#xff0c;電腦冒藍光&#xff0c;一直藍屏反復重啟&#xff0c;故只能重裝系統以解難題。 2.準備一個U盤&…

設計HTML5文檔結構

定義清晰、一致的文檔結構不僅方便后期維護和拓展&#xff0c;同時也大大降低了CSS和JavaScript的應用難度。為了提高搜索引擎的檢索率&#xff0c;適應智能化處理&#xff0c;設計符合語義的結構顯得很重要。 1、頭部結構 在HTML文檔的頭部區域&#xff0c;存儲著各種網頁元…

Python Opencv實踐 - 圖像屬性相關

import numpy as np import cv2 as cv import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) plt.imshow(img[:,:,::-1])#像素操作 pixel img[320,370] print(pixel)#只獲取藍色通道的值 pixel_blue img[320,370,0]…

【Hystrix技術指南】(7)故障切換的運作流程原理分析(含源碼)

背景介紹 目前對于一些非核心操作&#xff0c;如增減庫存后保存操作日志發送異步消息時&#xff08;具體業務流程&#xff09;&#xff0c;一旦出現MQ服務異常時&#xff0c;會導致接口響應超時&#xff0c;因此可以考慮對非核心操作引入服務降級、服務隔離。 Hystrix說明 官方…

Grounding DINO:根據文字提示檢測任意目標

文章目錄 1. 背景介紹2. 方法創新2.1 Feature Extraction and Enhancer2.2 Language-Guided Query Selection2.3 Cross-Modality Decoder2.4 Sub-Sentence Level Text Feature2.5 Loss Function3. 實驗結果3.1 Zero-Shot Transfer of Grounding DINO3.2 Referring Object Detec…

設備管理系統能起到什么作用?

在現代工業運營中&#xff0c;設備的高效管理和維護對于保障生產穩定運行和提升企業競爭力至關重要。而設備管理系統作為一種關鍵工具&#xff0c;能夠極大地提高企業的生產效率和設備維護的準確性。本文將深入探討設備管理系統的作用&#xff0c;以PreMaint設備數字化平臺為例…

Qt 對象序列化/反序列化

閱讀本文大概需要 3 分鐘 背景 日常開發過程中&#xff0c;避免不了對象序列化和反序列化&#xff0c;如果你使用 Qt 進行開發&#xff0c;那么有一種方法實現起來非常簡單和容易。 實現 我們知道 Qt 的元對象系統非常強大&#xff0c;基于此屬性我們可以實現對象的序列化和…

智能家居(3)---socket網絡控制線程封裝

封裝socket網絡線程實現對智能家居中各種燈光的控制 main.Pro(主函數) #include <stdio.h> #include "controlDevice.h" #include "inputCommand.h" #include <pthread.h>struct Devices *pdeviceHead NULL; //設備工廠鏈表…

ES踩坑記錄之UNASSIGNED分片無法恢復

問題背景 換節點 我們線上有一套ES集群&#xff0c;三臺機器&#xff0c;共運行了6個節點。一直在線上跑了幾個月也一直沒出什么問題。然而好巧不巧&#xff0c;就在昨天&#xff0c;集群中的3號節點磁盤出現故障&#xff0c;導致機器直接癱瘓。本來大家覺得問題不大&#xf…

HTML和JavaScript實現一個簡單的計算器

使用HTML和JavaScript實現一個簡單的計算器。 一、繪制鍵盤 <!DOCTYPE html> <html> <head><title>Simple Calculator</title><style>.calculator {display: grid;grid-template-columns: repeat(4, 1fr);grid-gap: 5px;padding: 10px;}.…

【elementUi】繪制自定義表格、繪制曲線表格

要求繪制下圖系列表格&#xff1a; 實現步驟: 1.繪制樹&#xff0c;實現樹勾選字段—>表格繪制字段 邏輯&#xff1a; 樹&#xff1a;check-change“treeChart.handleCheckChange” 綁定點擊選擇事件&#xff0c;改變data.column3數據項&#xff1b;表格:columns"data…

基于 Nginx All In One 的 Outline Wiki 部署方法

1. Outline 簡介 官網&#xff1a;https://www.getoutline.com/ Outline 是一個開源的知識庫和團隊協作工具&#x1f9e0;&#xff0c;旨在幫助團隊共享、組織和協作文檔&#x1f4dd;。它提供了一個簡潔的界面&#xff0c;使用戶能夠輕松創建、編輯和查看文檔。 以下是 Out…

Ajax 筆記(四)—— Ajax 進階

筆記目錄 4. Ajax 進階4.1 同步代碼和異步代碼4.2 回調函數地獄4.2.1 解決方法一&#xff1a;Promise 鏈式調用4.2.2 解決方法二&#xff1a;async 函數和 await 4.3 Promise.all 靜態方法4.4 事件循環4.4.1 事件循環4.4.2 宏任務與微任務 4.5 案例4.5.1 案例一-商品分類4.5.2 …

讀書筆記 |【項目思維與管理】? 項目成為一種生存方式

讀書筆記 |【項目思維與管理】? 項目成為一種生存方式 一、理解項目固有的挑戰二、項目對企業的價值三、知識型企業的經營邏輯四、做項目管理的推進者 &#x1f496;The Begin&#x1f496;點點關注&#xff0c;收藏不迷路&#x1f496; 項目無處不在&#xff0c;項目已經成為…

智慧建筑工地平臺,通過信息化技術、物聯網、人工智能技術,實現對施工全過程的實時監控、數據分析、智能管理和優化調控

智慧工地是指通過信息化技術、物聯網、人工智能技術等手段&#xff0c;對建筑工地進行數字化、智能化、網絡化升級&#xff0c;實現對施工全過程的實時監控、數據分析、智能管理和優化調控。智慧工地的建設可以提高工地的安全性、效率性和質量&#xff0c;降低施工成本&#xf…

分布式唯一ID實戰

目錄 一、UUID二、數據庫方式1、數據庫生成之簡單方式2、數據庫生成 - 多臺機器和設置步長&#xff0c;解決性能問題3、Leaf-segment 方案實現4、雙 buffer 優化5、Leaf高可用容災 三、基于Redis實現分布式ID四、雪花算法 一、UUID UUID的標準形式包含32個16進制數字&#xff…

Word 2019打開.doc文檔后圖片和公式不顯示(呈現為白框)的解決辦法

Word 2019打開.doc文檔后圖片和公式不顯示&#xff08;呈現為白框&#xff09;的解決辦法 目錄 Word 2019打開.doc文檔后圖片和公式不顯示&#xff08;呈現為白框&#xff09;的解決辦法一、問題描述二、解決方法1.打開 WORD 2019&#xff0c;點擊菜單中的“文件”&#xff1b;…

微服務05-Sentinel流量防衛兵

隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分布式、多語言異構化服務架構的流量治理組件,主要以 流量 為切入點,從流量路由、流量控制、流量整形、熔斷降級、系統自適應過載保護、熱點流量防護等多個維度來幫助開發者保障微服務的穩定性。 S…

FBB簡介

FBB FBB簡介FBB組網名詞解釋FBB組網&#xff08;從下至上&#xff09;小結 FBB簡介 FBB&#xff1a;Fixed BroadBand&#xff0c;固定寬帶網絡&#xff0c;是一種依托線纜介質進行傳輸的地面有線網絡&#xff0c;區分與MBB&#xff0c;MBB是移動網絡。 MBB是天上一張網&#x…

學習Vue:簡介和優勢

什么是 Vue.js&#xff1f; Vue.js 是一個用于構建用戶界面的漸進式 JavaScript 框架。它專注于視圖層&#xff0c;并且可以輕松地集成到現有的項目中。Vue.js 的設計理念是漸進式&#xff0c;這意味著您可以根據項目的需要逐步引入 Vue.js&#xff0c;從而更好地控制應用的復…