当前位置: 首页 > news >正文

Android Ble蓝牙App(七)扫描过滤

Ble蓝牙App(七)扫描过滤

  • 前言
  • 目录
  • 正文
    • 一、增加菜单
    • 二、使用MMKV
      • ① 添加依赖
      • ② 封装MMKV
      • ③ 使用MMKV
    • 三、过滤空设备名
    • 四、过滤Mac地址
    • 五、过滤RSSI
    • 六、源码

前言

  在上一篇文章中了解了MTU的相关知识以及对于设备操作信息的展示,本篇文章中将增加扫描设备的过滤功能让你更方便的扫描想要找的低功耗蓝牙设备。

在这里插入图片描述

目录

  • Ble蓝牙App(一)扫描
  • Ble蓝牙App(二)连接与发现服务
  • Ble蓝牙App(三)特性和属性
  • Ble蓝牙App(四)UI优化和描述符
  • Ble蓝牙App(五)数据操作
  • Ble蓝牙App(六)请求MTU与显示设备信息
  • Ble蓝牙App(七)扫描过滤

正文

  增加扫描过滤主要就是让扫描设备的时候更方便找到想要的设备,下面我们来看有哪些功能的增加。

一、增加菜单

  为了不占用扫描页面的空间,我打算通过添加菜单来进行扫描的过滤操作,那么首先我们在menu下增加一个menu_scan.xml文件,代码如下所示:

<menu xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@+id/item_filter_null"android:checkable="true"android:title="过滤空设备名" /><itemandroid:id="@+id/item_filter_mac"android:checkable="true"android:title="过滤Mac地址" /><itemandroid:id="@+id/item_filter_rssi"android:checkable="true"android:title="过滤RSSI" /></menu>

菜单中有三个Item,看一下预览效果图:

在这里插入图片描述
  三个Item都是选中Item,选中表示这个过滤功能项启用,可以全部都选中,也可以任意选择,之后我们进入到ScanActivity,首先是创建菜单和菜单选中,修改地方有三处:

第一处:在onCreate()函数中增加支持ActionBar,代码如下所示:

    override fun onCreate(savedInstanceState: Bundle?) {...setSupportActionBar(binding.toolbar)...}

第二处:在ScanActivity中重写onCreateOptionsMenu()函数,代码如下所示:

	private lateinit var mMenu: Menuoverride fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_main, menu)mMenu = menureturn true}

创建选项菜单,再创建一个mMenu变量,在后面会用到的这个变量。

第三处:在ScanActivity中重写onOptionsItemSelected()函数,代码如下所示:

    override fun onOptionsItemSelected(item: MenuItem): Boolean {when(item.itemId) {R.id.item_filter_null -> { // 过滤空设备名称}R.id.item_filter_mac -> { // 过滤Mac地址}R.id.item_filter_rssi -> { // 过滤RSSI}}return true}

现在三个Item的点击事件中什么都不做,我们一步一步给它加上,现在菜单就创建好了。

二、使用MMKV

  因为我们修改的菜单项会涉及到保存过滤设置的功能,所以需要将一些参数报错到手机中,那么我们可以使用SP、DataStore等方式,但是这里我是用MMKV,主要是因为用起来比较的方便,下面我们来使用MMKV。

① 添加依赖

  MMKV是腾讯的一个开源项目,已经发布在mavenCentral()仓库中了,我们在App中使用只需要在app模块下的build.gradle中的dependencies{}闭包中添加如下依赖代码即可:

dependencies {...//mmkvimplementation 'com.tencent:mmkv:1.2.14'
}

然后点击Sync Now,同步一下,添加依赖就完成了。

② 封装MMKV

  针对于MMKV的使用其实非常简单,就是两步,先初始化,然后使用就好了,那么为了使用的更方便,我们可以简单封装一下MMKV,做成一个工具类,下面我们在com.llw.goodble包下新建一个utils包,utils包下新建一个MVUtils类,代码如下所示:

object MVUtils {val mmkv = MMKV.defaultMMKV()fun put(key: String, value: Any): Boolean {return when (value) {is String -> mmkv.encode(key, value)is Float -> mmkv.encode(key, value)is Boolean -> mmkv.encode(key, value)is Int -> mmkv.encode(key, value)is Long -> mmkv.encode(key, value)is Double -> mmkv.encode(key, value)is ByteArray -> mmkv.encode(key, value)is Parcelable -> mmkv.encode(key, value)else -> false}}fun put(key: String, sets: Set<String>?): Boolean {if (sets == null) {return false}return mmkv.encode(key, sets)}fun getInt(key: String, defaultValue: Int = 0) = mmkv.decodeInt(key, defaultValue)fun getDouble(key: String, defaultValue: Double = 0.00) = mmkv.decodeDouble(key, defaultValue)fun getLong(key: String, defaultValue: Long = 0L) = mmkv.decodeLong(key, defaultValue)fun getBoolean(key: String, defaultValue: Boolean = false) = mmkv.decodeBool(key, defaultValue)fun getFloat(key: String, defaultValue: Float = 0F) = mmkv.decodeFloat(key, defaultValue)fun getByteArray(key: String) = mmkv.decodeBytes(key)fun getString(key: String, defaultValue: String = "") = mmkv.decodeString(key, defaultValue)inline fun <reified T : Parcelable> getParcelable(key: String) =mmkv.decodeParcelable(key, T::class.java)fun getStringSet(key: String) = mmkv.decodeStringSet(key, Collections.emptySet())fun removeKey(key: String) = mmkv.removeValueForKey(key)fun clearAll() = mmkv.clearAll()
}

  这里实际上大体就分为三个部分,首先是初始化,然后是数据的存和取,最后是清除数据,是不是很简单呢?

③ 使用MMKV

  使用MMKV,首先需要做的就是初始化,我们需要在BleApponCreate()函数中进行初始化,代码如下所示:

    override fun onCreate() {...//mmkv初始化MMKV.initialize(this)}

  使用MMKV同样是采用键值对的形式,那么基于我们的菜单功能,我们需要增加一些键,在BleConstant中增加如下常量,代码如下所示:

    //过滤RSSIconst val FILTER_RSSI_FLAG = "filterRssiFlag"//RSSI 值const val FILTER_RSSI_VALUE = "filterRssiValue"//过滤空设备名const val FILTER_NULL_FLAG = "filterNullFlag"//是否过滤Mac地址const val FILTER_MAC_FLAG = "filterMacFlag"//需要过滤的Mac地址const val FILTER_MAC_VALUE = "filterMacValue"

下面我们修改ScanActivity中的onCreateOptionsMenu()函数,代码如下所示:

    override fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_scan, menu)mMenu = menumMenu.findItem(R.id.item_filter_rssi).isChecked = MVUtils.getBoolean(FILTER_RSSI_FLAG)if (MVUtils.getBoolean(FILTER_RSSI_FLAG)) {mMenu.findItem(R.id.item_filter_rssi).title ="过滤RSSI:-" + MVUtils.getInt(FILTER_RSSI_VALUE, 100)}mMenu.findItem(R.id.item_filter_null).isChecked = MVUtils.getBoolean(FILTER_NULL_FLAG)mMenu.findItem(R.id.item_filter_mac).isChecked = MVUtils.getBoolean(FILTER_MAC_FLAG)return true}

  在这里的代码就是在创建菜单的时候,判断一下保存的参数,是否需要选中Item,可以修改Item的选中状态和标题内容,这里就是获取参数。

三、过滤空设备名

  下面我们来保存参数,修改onOptionsItemSelected()函数中的代码:

            R.id.item_filter_null -> { // 过滤空设备名称if (bleCore.isScanning()) stopScan()val filterNull = MVUtils.getBoolean(FILTER_NULL_FLAG)MVUtils.put(FILTER_NULL_FLAG, !filterNull)mMenu.findItem(R.id.item_filter_null).isChecked = MVUtils.getBoolean(FILTER_NULL_FLAG)showMsg(if (MVUtils.getBoolean(FILTER_NULL_FLAG)) "过滤空设备名称的设备" else "保留空设备名称的设备")if (!bleCore.isScanning()) startScan()}

  这里看到就是在点击过滤空设备Item时,首先停止扫描,然后获取参数值,再保存,根据值设置Item是否选中,最后开始扫描,那么我们怎么过滤这个空设备名称的设备呢?还需要修改扫描回调中的代码:

    override fun onScanResult(result: ScanResult) {//过滤空设备名if (MVUtils.getBoolean(FILTER_NULL_FLAG)) {if (result.scanRecord!!.deviceName == null) {return}if (result.scanRecord!!.deviceName!!.isEmpty()) {return}}...}

  这里我们只需要在原有的条件上再增加一个判断即可,因为缺省值是false,所以如果是不过滤空设备名就不会执行判断里面空处理和空设备名处理,看一下运行的效果。

在这里插入图片描述
  我们看到默认是不过滤空设备名称的,当选中过滤空设备名后就会过滤设备名称为空的设备,只不过我们这里对于空设备名称的设备显示的UI还没有处理的很好,下面我们简单改一下,将onScanResult()函数中的这一行代码:

	val bleDevice = BleDevice(result.scanRecord!!.deviceName, result.device.address, result.rssi, result.device)

改成

	val realName = result.scanRecord?.deviceName?.let { it.ifEmpty { BleConstant.UNKNOWN_DEVICE } } ?: BleConstant.UNKNOWN_DEVICEval bleDevice = BleDevice(realName, result.device.address, result.rssi, result.device)

  这里改的目的就是首先判断获取的设备名是否为空,如果为空则返回一个Unknown device作为设备名称,不为空则检查是否为空字符串,是的话也返回Unknown device,不是则返回本身设备名称,再运行一下就可以了。

四、过滤Mac地址

  下面我们要做过滤Mac地址,那么要过滤Mac地址,首先要输入Mac地址,那么我们可以写一个弹窗来进行输入的工作,在layout下创建一个dialog_settings_mac.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="过滤Mac地址" /><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"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/et_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Mac Address"android:lines="1"android:singleLine="true" /></com.google.android.material.textfield.TextInputLayout><CheckBoxandroid:id="@+id/cb_format_check"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Mac地址格式检查"app:layout_constraintStart_toStartOf="@+id/data_layout"app:layout_constraintTop_toBottomOf="@+id/data_layout" /><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_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/cb_format_check" /></androidx.constraintlayout.widget.ConstraintLayout>

这个布局中有一个检查Mac地址正确性的复选框,同样我们需要在BleUtils中增加一个函数,代码如下所示:

    fun isValidMac(macStr: String) = Regex("([A-Fa-f0-9]{2}[:]){5}[A-Fa-f0-9]{2}").matches(macStr)

下面我们回到ScanActivity中,写一个showSettingMacDialog()函数,代码如下所示:

    private fun showSettingMacDialog() {val dialog = BottomSheetDialog(this, R.style.BottomSheetDialogStyle)val macBinding = DialogSettingMacBinding.inflate(layoutInflater)macBinding.btnPositive.setOnClickListener {val inputData = macBinding.etData.text.toString()if (inputData.isEmpty()) {macBinding.dataLayout.error = "请输入Mac地址"return@setOnClickListener}if (macBinding.cbFormatCheck.isChecked) {if (!BleUtils.isValidMac(inputData)) {macBinding.dataLayout.error = "请输入正确的Mac地址"return@setOnClickListener}}if (bleCore.isScanning()) stopScan()MVUtils.put(FILTER_MAC_VALUE, inputData)MVUtils.put(FILTER_MAC_FLAG, true)mMenu.findItem(R.id.item_filter_mac).isChecked = trueshowMsg("过滤Mac地址")if (!bleCore.isScanning()) startScan()dialog.dismiss()}macBinding.btnNegative.setOnClickListener {dialog.dismiss()}dialog.setContentView(macBinding.root)dialog.show()}

  弹窗中点击确定按钮就会先检查一遍,然后就会保存Mac地址,再保存过滤标识,然后我们修改一下过滤Mac地址Item的点击事件,代码如下所示:

            R.id.item_filter_mac -> { // 过滤Mac地址if (MVUtils.getBoolean(FILTER_MAC_FLAG)) {mMenu.findItem(R.id.item_filter_mac).isChecked = falseMVUtils.put(FILTER_MAC_FLAG, false)MVUtils.put(FILTER_MAC_VALUE, "")showMsg("不过滤设备地址")} else {showSettingMacDialog()}}

  首先判断是否过滤,有的话就不再过滤,没有的话就显示输入Mac地址弹窗,如果过滤了,我们就需要在扫描回调函数中增加一个过滤的选项。

    override fun onScanResult(result: ScanResult) {//过滤空设备名...//过滤Mac地址if (MVUtils.getBoolean(FILTER_MAC_FLAG)) {val filterMac: String? = MVUtils.getString(FILTER_MAC_VALUE, "")if (filterMac!!.isNotEmpty()) {if (!result.device.address.contains(filterMac)) return}}...}

  过滤的位置可以放在过滤空设备名称之后或者之前都可以,最后还需要修改onCreateOptionsMenu()函数中的代码如下所示:

    override fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_scan, menu)mMenu = menumMenu.findItem(R.id.item_filter_null).isChecked = MVUtils.getBoolean(FILTER_NULL_FLAG)mMenu.findItem(R.id.item_filter_mac).isChecked = MVUtils.getBoolean(FILTER_MAC_FLAG)return true}

增加了一个对于Mac地址Item项是否选中的判断,下面我们可以运行看看,我们的过滤是否有效果。

在这里插入图片描述

这样过滤Mac地址就做好了,下面过滤RSSI信号强度。

五、过滤RSSI

  与过滤Mac地址一样,过滤RSSI首先要做的就是设置RSSI,对此,我们同样在layout下创建一个dialog_settings_rssi.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"android:layout_width="match_parent"android:layout_height="match_parent"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="过滤RSSI" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="16dp"android:layout_marginTop="16dp"android:text="RSSI:"android:textColor="@color/black"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/toolbar" /><androidx.appcompat.widget.AppCompatSeekBarandroid:id="@+id/sb_rssi"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:max="100"android:min="35"android:progress="100"android:progressTint="@color/orange"android:thumbTint="@color/dark_orange"app:layout_constraintBottom_toBottomOf="@+id/textView"app:layout_constraintEnd_toStartOf="@+id/tv_rssi"app:layout_constraintStart_toEndOf="@+id/textView"app:layout_constraintTop_toTopOf="@+id/textView" /><TextViewandroid:id="@+id/tv_rssi"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="16dp"android:text="-100 dBm"android:textColor="@color/black"app:layout_constraintBottom_toBottomOf="@+id/textView"app:layout_constraintEnd_toEndOf="parent" /><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_weight="1"android:text="确定"app:layout_constraintEnd_toEndOf="@+id/tv_rssi"app:layout_constraintTop_toBottomOf="@+id/tv_rssi" />
</androidx.constraintlayout.widget.ConstraintLayout>

然后就是在ScanActivity中增加一个showSettingRssi()函数,代码如下所示:

    private fun showSettingRssi() {val dialog = BottomSheetDialog(this, R.style.BottomSheetDialogStyle)val rssiBinding = DialogSettingRssiBinding.inflate(layoutInflater)var progress = 100rssiBinding.sbRssi.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {@SuppressLint("SetTextI18n")override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {rssiBinding.tvRssi.text = "-$progress dBm"}override fun onStartTrackingTouch(seekBar: SeekBar) {}override fun onStopTrackingTouch(seekBar: SeekBar) {progress = seekBar.progress}})val rssi: Int = MVUtils.getInt(FILTER_RSSI_VALUE, 100)rssiBinding.sbRssi.progress = rssirssiBinding.tvRssi.text = String.format("-%s dBm", rssi)rssiBinding.btnPositive.setOnClickListener {//保存if (bleCore.isScanning()) stopScan()MVUtils.put(FILTER_RSSI_FLAG, true)//保存设置的RSSI值MVUtils.put(FILTER_RSSI_VALUE, progress)mMenu.findItem(R.id.item_filter_rssi).isChecked = truemMenu.findItem(R.id.item_filter_rssi).title = "过滤RSSI:-$progress"showMsg("过滤RSSI:-" + progress + "dBm")if (!bleCore.isScanning()) startScan()dialog.dismiss()}rssiBinding.btnNegative.setOnClickListener { dialog.dismiss() }dialog.setContentView(rssiBinding.root)dialog.show()}

  在点击确定按钮的时候,保存设置的RSSI信号强度值,如果没有设置就是默认的值,然后我们修改一下过滤RSSI Item的点击事件,代码如下所示:

            R.id.item_filter_rssi -> { // 过滤RSSIif (MVUtils.getBoolean(FILTER_RSSI_FLAG)) {if (bleCore.isScanning()) stopScan()//关闭过滤RSSIMVUtils.put(FILTER_RSSI_FLAG, false)mMenu.findItem(R.id.item_filter_rssi).isChecked = falseMVUtils.put(FILTER_RSSI_VALUE, 100)showMsg("取消过滤RSSI")if (!bleCore.isScanning()) startScan()} else {showSettingRssi()}}

  当前已有过滤RSSI,再次点击时就会取消过滤的信息,知道你再次设置RSSI过滤值,接下来就是扫描回调中,根据这个设置项进行一次过滤:

    override fun onScanResult(result: ScanResult) {//过滤Mac地址...//过滤RSSIif (MVUtils.getBoolean(FILTER_RSSI_FLAG)) {val rssi: Int = -MVUtils.getInt(FILTER_RSSI_VALUE, 100)if (result.rssi < rssi) {return}}...}

  最后为了保存设置项,是我们再次打开App时,UI上是正确的,我们修改onCreateOptionsMenu()函数,代码如下所示:

    override fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_scan, menu)mMenu = menumMenu.findItem(R.id.item_filter_null).isChecked = MVUtils.getBoolean(FILTER_NULL_FLAG)mMenu.findItem(R.id.item_filter_mac).isChecked = MVUtils.getBoolean(FILTER_MAC_FLAG)mMenu.findItem(R.id.item_filter_rssi).isChecked = MVUtils.getBoolean(FILTER_RSSI_FLAG)if (MVUtils.getBoolean(FILTER_RSSI_FLAG)) {mMenu.findItem(R.id.item_filter_rssi).title ="过滤RSSI:-" + MVUtils.getInt(FILTER_RSSI_VALUE, 100)}return true}

运行一下,看看效果:

在这里插入图片描述

  关于扫描过滤的功能就写好了,本文内容介绍。

六、源码

如果对你有所帮助的话,不妨 StarFork,山高水长,后会有期~

源码地址:GoodBle

相关文章:

Android Ble蓝牙App(七)扫描过滤

Ble蓝牙App&#xff08;七&#xff09;扫描过滤 前言目录正文一、增加菜单二、使用MMKV① 添加依赖② 封装MMKV③ 使用MMKV 三、过滤空设备名四、过滤Mac地址五、过滤RSSI六、源码 前言 在上一篇文章中了解了MTU的相关知识以及对于设备操作信息的展示&#xff0c;本篇文章中将增…...

小程序当前页面栈以及跳转

1.调用页面栈刷新接口 let pages getCurrentPages(); //当前页面栈 if (pages.length > 1) { let beforePage pages[pages.length - 2]; //获取上一个页面实例对象 beforePage.$vm.getActivityLi…...

jQuery获取表单的值val()

&#xff08;1&#xff09;页面中有很多元素&#xff0c;包括表单中的输入项&#xff0c;如输入文本框等&#xff1b;获取、设置、输入文本框的值&#xff1b;val()方法。 &#xff08;2&#xff09;也包括<p>、<span>等元素&#xff1b;获取、设置这些元素的文本…...

【专栏必读】数字图像处理(MATLAB+Python)专栏目录导航及学习说明

文章目录 第一章&#xff1a;绪论第二章&#xff1a;数字图像处理基础第三章&#xff1a;图像基本运算第四章&#xff1a;图像的正交变换第五章&#xff1a;图像增强第六章&#xff1a;图像平滑第七章&#xff1a;图像锐化第八章&#xff1a;图像复原第九章&#xff1a;图像形态…...

2023年非证券类投资银行业发展报告

第一章 行业概况 非证券投资银行业是一个专门为公司、政府和高净值个人提供金融服务的行业&#xff0c;与传统的证券投资银行不同&#xff0c;其主要业务不涉及证券交易&#xff0c;而是注重为客户提供咨询服务、融资和投资管理等服务。 非证券投资银行通常涉及的业务领域包括…...

Matlab 如何把频谱图的纵坐标设置为分贝刻度

Matlab 如何把频谱图的纵坐标设置为分贝刻度 Matlab代码如下&#xff1a; % 如何把频谱图的纵坐标设置为分贝刻度 % % pr2_2_6 clc; clear; close all;load pr2_2_6_sndata1.mat % 读入数据 X fft(y); % FFT n2 1:L/21; % 计算正频率…...

VUE写后台管理(2)

VUE写后台管理&#xff08;2&#xff09; 1.环境2.Element界面3.Vue-Router路由后台1.左导航栏2.上面导航条 1.环境 1.下载管理node版本的工具nvm&#xff08;Node Version Manager&#xff09; 2.安装node(vue工程的环境管理工具)&#xff1a;nvm install 16.13.0 3.安装vue工…...

RHCSA8.2

Node1 配置您的系统以使用默认存储库 配置您 的系统以使用默认存储库YUM 存储库已可以从 http://foundation0.ilt.example.com/dvd/BaseOS 和 http://foundation0.ilt.example.com/dvd/AppStream 使用配置您的系统&#xff0c;以将这些位置用作默认存储库[rootclear ~]# cat …...

修改linux中tomcat的端口

随便修改一个 以8055为例子 开放8081端口 firewall-cmd --permanent --add-port8081/tcp firewall-cmd --reload firewall-cmd --list-all...

学妹学Java(一)

⭐简单说两句⭐ 作者&#xff1a;后端小知识 CSDN个人主页&#xff1a;后端小知识 &#x1f50e;GZH&#xff1a;后端小知识 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; Hello&#xff0c;亲爱的各位友友们&#xff0c;好久不见&#xff0…...

湖南省副省长秦国文一行调研考察亚信科技

9月5日&#xff0c;湖南省人民政府党组成员、副省长秦国文一行到亚信科技调研考察&#xff0c;亚信科技高级副总裁陈武主持接待。 图&#xff1a;双方合影 在亚信科技创新展示中心&#xff0c;秦国文了解了亚信科技在5G、算力网络、人工智能、大数据等前沿领域的创新探索&…...

k8s部署redis 3主3从

k8s部署redis6节点&#xff0c;组成3主3从集群模式 一般来说&#xff0c;redis部署有三种模式。 单实例模式&#xff0c;一般用于测试环境。 哨兵模式 集群模式后两者用于生产部署 哨兵模式 在redis3.0以前&#xff0c;要实现集群一般是借助哨兵sentinel工具来监控master节点…...

Vue2安装vuex和vue-router报错处理

Vue2安装vuex和vue-router报错处理 Vue2.6安装VuexVue2.6安装vue-router Vue2.6安装Vuex 报错信息 处理方法 #查看vuex版本 npm view vuex versions --json #安装合适版本 npm install vuex3.6.2 --saveVue2.6安装vue-router 报错信息 处理方法 #查看vue-router版本 npm…...

算法leetcode|79. 单词搜索(rust重拳出击)

文章目录 79. 单词搜索&#xff1a;样例 1&#xff1a;样例 2&#xff1a;样例 3&#xff1a;提示&#xff1a;进阶&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 79. 单词搜索&#xff1a; …...

2023年高教社杯全国大学生数学建模竞赛参赛事项注意

MathClub数模资源&#xff0c;含专属思路 资源链接&#xff1a;点击这里获取众多数模资料、思路精讲、论文模板latex和word、学习书籍等 2023高教社杯数学建模国赛–赛前准备 一年一度的数学建模国赛要来啦&#xff01;&#xff01;&#xff01;小编仔细阅读了比赛官方网站上…...

数学建模--逻辑回归算法的Python实现

首先感谢CSDN上发布吴恩达的机器学习逻辑回归算法任务的各位大佬. 通过大佬的讲解和代码才勉强学会. 这篇文章也就是简单记录一下过程和代码. CSDN上写有关这类文章的大佬有很多,大家都可以多看一看学习学习. 机器学习方面主要还是过程和方法. 这篇文章只完成了线性可分方面的任…...

Qt6_贪吃蛇Greedy Snake

贪吃蛇Greedy Snake 1分析 首先这是一个贪吃蛇界面&#xff0c;由一个长方形边框和一只贪吃蛇组成 默认开局时&#xff0c;贪吃蛇身体只有3个小方块&#xff0c;使用画笔画出 1.1如何移动 对于蛇的移动&#xff0c;有2种方法 在一定时间范围内(定时器)&#xff0c;未对游戏…...

Credo推出业界首款单片集成CMOS VCSEL驱动器的800G光DSP芯片

针对AOC及短距&#xff08;SR&#xff09;光模块优化的新型Credo DSP&#xff0c;适用于下一代超大规模数据中心/AI应用 加州圣何塞和中国深圳&#xff0c;2023年9月6日——Credo Technology&#xff08;纳斯达克股票代码&#xff1a;CRDO&#xff09;今日发布两款新品&#x…...

【经验分享】如何使用VSCode对比两个文件

问题&#xff1a; 当有两个不同版本的文件&#xff0c;如何使用VSCode对比两个文件 解决办法 长按ctrl选择想要对比的两个文件-----右键选择将已选项进行比较----大功告成 大功告成...

从裸机开始安装ubuntu系统到安装NVIDIA驱动

这篇文章为总结类文章&#xff0c;更多的是把各个博主的内容总结一下&#xff0c;形成一套端到端的方法&#xff0c;主要内容包括&#xff1a; 安装ubuntu22.04版本(含启动U盘制作)配置ssh、固定ip和端口号安装NVIDIA驱动安装cuda11.7和cudnn8.6 文章目录 一、安装ubuntu22.041…...

索尼 toio™ 应用创意开发征文|小巧机器,大无限,探索奇妙世界

文章目录 前言微型机器人的未来&#xff1a;toio™小机器人简介toio™小机器人&#xff1a;创新功能一览toio™小机器人&#xff1a;多领域的变革者toio™小机器人贪吃蛇游戏代码实现写在最后 前言 当我们谈到现代科技的创新时&#xff0c;往往会联想到复杂的机器和高级的编程…...

什么牌子的led台灯质量好?热门的Led护眼台灯推荐

led台灯有环保无污染、耗能低、长寿命等优点&#xff0c;适合用在阅读、书写、批阅等办公或学习的场所。而挑选LED台灯时&#xff0c;分散光挡板做的比较好的优先选择&#xff0c;能分散大量蓝光&#xff0c;对眼睛危害较小。下面&#xff0c;小编为大家推荐五款质量好的led护眼…...

预推免,保研------长安大学保内,附加分面试准备【记录帖】

&#x1f680;长安大学——人工智能系——程惠泽 &#x1f68c;前六学期专业排名&#xff1a;7/82 &#x1f68c;信息门户GPA&#xff1a;3.94 &#x1f68c;平均成绩&#xff1a;89.83 &#x1f68c;加权成绩&#xff1a;89.15 / ☁️本人比较菜&#xff0c;只能保研本校&…...

Linux开源防病毒引擎ClamAV

ClamAV官方地址&#xff1a;https://www.clamav.net 它支持Linux、BSD、windows、Mac OS X等系统。 在CentOS 8&#xff08;Tencent OS 3.1&#xff09;安装非常便利&#xff0c;可以使用yum。 yum install clamav 安装成功&#xff0c;就可以使用它进行病毒扫描检查了。 c…...

Java复习-25-单例设计模式

单例设计模式 目的&#xff08;使用场景&#xff09; 在实际开发下&#xff0c;会存在一种情况&#xff1a;某一种类在程序的整个生命周期中&#xff0c;只需要实例化一次就足够了。例如&#xff0c;系统数据类&#xff0c;由于操作系统只有一个&#xff0c;因此在程序初始化…...

博客系统自动化测试项目实战(测试系列9)

目录 前言&#xff1a; 1.博客前端页面测试用例图 2.测试用例的代码实现 2.1登录页面的测试 2.2博客列表页面的测试 2.3写博客测试 2.4博客详情页面的测试 2.5已发布博客的标题和时间的测试 2.6注销用户的测试 结束语&#xff1a; 前言&#xff1a; 之前小编给大家讲…...

华纳云:Linux的底层体系结构是怎样的

Linux操作系统的底层体系结构是一个开源的Unix-like操作系统内核&#xff0c;通常称为Linux内核(Linux Kernel)。下面是Linux底层体系结构的主要组成部分和工作原理&#xff1a; 内核&#xff08;Kernel&#xff09;&#xff1a; Linux的核心部分是内核&#xff0c;它是操作系统…...

SpringMVC常用注解介绍及参数传递说明

前言 上一篇文章介绍了SpringMVC是什么以及它的工作流程和核心组件&#xff0c;介绍入门示例时&#xff0c;提到了RequestMapping注解&#xff0c;那么这篇文章就来介绍SpringMVC中更多的常用的注解&#xff0c;以及它的参数传递。 一. SpringMVC常用注解 1.1 RequestParam …...

4 个你可能不知道的 Python 迭代工具过滤器函数

推荐&#xff1a;使用 NSDT场景编辑器 快速搭建3D应用场景 当您只想循环遍历迭代器、检索序列中的元素并处理它们时&#xff0c;这些元素特别有用 - 所有这些都无需将它们存储在内存中。今天我们将学习如何使用以下四个迭代工具过滤器函数&#xff1a; filterfalsetakewhiledr…...

Scrapy简介-快速开始-项目实战-注意事项-踩坑之路

scrapy项目模板地址&#xff1a;https://github.com/w-x-x-w/Spider-Project Scrapy简介 Scrapy是什么&#xff1f; Scrapy是一个健壮的爬虫框架&#xff0c;可以从网站中提取需要的数据。是一个快速、简单、并且可扩展的方法。Scrapy使用了异步网络框架来处理网络通讯&…...

沈阳 网站建设/网站网页设计

前言&#xff1a;打脸了&#xff0c;前脚刚说过要跟Servlet正式告别。结果最近的面试被问到了同一个Servlet可不可以被映射到多个URL上&#xff0c;也就是如何用一个Servlet实现多个功能。 前置知识&#xff1a; Servlet容器如何处理请求资源路径&#xff1f; 1、这个地址 ht…...

数据库性质的网站怎么做/廊坊seo优化排名

数论的基本知识 本文将简单地介绍有关整数集合Z{…,-2,-1,0,1,2,…}和自然数集合N{0,1,2,…}的最基本的数论概念。 可除性与约数 一个整数能被另一个整数整除的概念是数论中的一个中心概念&#xff0c;记号d|a&#xff08;读作“d 除a”&#xff09;意味着对某个整数k&#xff…...

江西奶茶加盟网站建设/广告联盟论坛

sort 命令  用途  排序文件、对已排序的文件进行合并&#xff0c;并检查文件以确定它们是否已排序。  语法  sort [ -A ] [ -b ] [ -c ] [ -d ] [ -f ] [ -i ] [ -m] [ -n ] [ -r ] [ -u ] [ -o OutFile ] [ -t Character ] [ -T Directory ] [ -y [ Kilobytes ] ] [ -…...

公司设计网站需要多少钱/中国新闻

mina框架的使用非常简单&#xff0c;并且功能强大&#xff0c;将复杂的NIO操作封装成一套成熟的框架&#xff0c;在mina的使用中&#xff0c;我们只需要实现编解码器、信息处理器等就可以建立一个支持NIO通信的B/S程序。但是了解mina的源码对我们认识这个框架以及NIO都有很大帮…...

烟台开发区建设业联合会网站/百度助手app下载安装

1696:逆波兰表达式 总时间限制: 1000ms 内存限制: 65536kB描述逆波兰表达式是一种把运算符前置的算术表达式&#xff0c;例如普通的表达式2 3的逆波兰表示法为 2 3。逆波兰表达式的优点是运算符之间不必有优先级关系&#xff0c;也不必用括号改变运算次序&#xff0c;例如(2 …...

投资集团网站建设/西安seo培训学校

最近工作中&#xff0c;接触到了Java网络编程方面的东西&#xff1a;Socket、NIO、MongoDB等&#xff0c;也看了tomcat的源码&#xff0c;也加强了线程方面的知识&#xff0c;也使用了MINA这样的框架。感觉获益良多&#xff0c;原本技术上的薄弱环节也在慢慢提高&#xff0c;很…...