已解决:安卓,怎么优雅接入科大讯飞语音评测功能?

写在前面:

网上关于讯飞接入的博客都很少,按说讯飞都是业界翘楚,不知为何,很少搜索到精品,一搜就是一个要求开会员的博客,我也是醉了。讯飞提供的文档也是不清晰,我是摸着石头过河,过来了,我就给大家总结一个方法,给大家免费看,只要是我的粉丝,我永远给大家免费的阅读支持。

之前接入过web端的语音评测,那叫个麻烦建议大家不要轻易尝试,如果大家有必要的需求,可以尝试,但是建议不要玩,容易入坑。文章结尾有对各平台使用讯飞的一些看法和建议,需要的可以到文章末尾去看。

正文开始:

首先,我们从官网上下载demo,此时如果你还没有注册讯飞账号,那你先去注册一个开发者账号,不得不说,作为大公司,讯飞还是有些气量的,每天给账号500个免费的评测额度,(数量不知道我理解的是否正确,至少我已经免费用了一年了),然后创建自己的应用,如下图创建好以后就有了appid,这些不细说了,讯飞的文档主要就是介绍这些了,至于技术上的事儿基本没咋提
在这里插入图片描述
然后我们开始打包下载讯飞的sdk,我们点击上图的应用名称,然后进入到如下页面,点击下载sdk,这里的sdk会把我们生成的应用秘钥打包进去,我们只需要在app中配置一个appid就可以了。
在这里插入图片描述
然后我们点亮我们需要的功能,点击下载就可以,我选择了语音合成和语音测评功能,如下图,不建议选择新版,就用普通版本就可以,功能接口都一样,新版盲猜有坑:
在这里插入图片描述
下载完以后,打开文件夹,会看到一堆文件夹,我们需要的就是libs,其他的不需要,也不需要运行他说的sample,因为运行这个项目可能会很耗费时间,没必要。但是我们还是可以使用AS打开这个sample项目,以便于我们查找一些功能的使用方法,当然这是我的操作,你们也可以只看我的教程,不过我的教程只讲评测的功能,其实有了评测的经验,再做别的也手到擒来。
在这里插入图片描述
首先我们新建一个安卓项目,添加阿里云的镜像依赖,然后重新下载依赖,然后我们把上图的libs中的所有内容拷贝至Android工程的libs目录下。如下图所示:
在这里插入图片描述
在这里插入图片描述
然后,我们鼠标右键Msc.jar文件,选择一个Add As Library,将jar包添加到库里,我已经添加过了,弹不出来这个选项所以不截图了。
同时,我们在main包下创建如下图所示的文件夹,并且将上述sdk中的文件复制过来,如下图:
在这里插入图片描述
然后我们开始开发:
打开清单文件,然后添加文档中提到的安卓权限:

<!--连接网络权限,用于执行云端语音能力 -->
<uses-permission android:name="android.permission.INTERNET"/>
<!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<!--读取网络信息状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!--获取当前wifi状态 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!--允许程序改变网络连接状态 -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<!--读取手机信息权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!--读取联系人权限,上传联系人需要用到此权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<!--外存储写权限,构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--外存储读权限,构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!--配置权限,用来记录应用配置信息 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<!--手机定位信息,用来为语义等功能提供定位,提供更精准的服务-->
<!--定位信息是敏感信息,可通过Setting.setLocationEnable(false)关闭定位请求 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!--如需使用人脸识别,还要添加:摄相头权限,拍照需要用到 -->
<uses-permission android:name="android.permission.CAMERA" />

然后,需要混淆的话,添加一下这个,不添加会将讯飞sdk同步混淆导致闪退,如果不配置混淆就可以忽略。

-keep class com.iflytek.**{*;}
-keepattributes Signature

然后进行初始化:将如下代码行添加到应用的自定义Application或者调用评测的Activity的oncreate方法中,context用this就行:

// 将“12345678”替换成您申请的APPID,申请地址:http://www.xfyun.cn
// 请勿在“=”与appid之间添加任何空字符或者转义符
SpeechUtility.createUtility(context, SpeechConstant.APPID +"=12345678");

然后就开始调用关键方法:
我把封装的类贴出来吧,可以拿来即用,我是通过h5调用的,使用activity也是可以直接调用的:

package com.lz.english.activity.webview

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.webkit.JavascriptInterface
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.gson.Gson
import com.iflytek.cloud.EvaluatorListener
import com.iflytek.cloud.EvaluatorResult
import com.iflytek.cloud.SpeechConstant
import com.iflytek.cloud.SpeechError
import com.iflytek.cloud.SpeechEvaluator
import com.lz.english.activity.WebViewActivity
import com.lz.english.databinding.ActivityWebviewBinding
import com.lz.english.result.xml.XmlResultParser
import com.lz.english.utils.ToastUtils
import org.greenrobot.eventbus.EventBus

private const val s1 = "ise_unite"

private const val s = "ise_unite"

class JsBridge(
    context: WebViewActivity,
    binding: ActivityWebviewBinding,
    mIse: SpeechEvaluator
) {
    private val TAG = "JsBride"
    // 创建语音评测对象,使用lateinit关键字标记为非空
//    private lateinit var mIse:SpeechEvaluator ;
    companion object {
        private const val REQUEST_RECORD_AUDIO_PERMISSION = 1
    }
    private lateinit var context: WebViewActivity
    private lateinit var binding: ActivityWebviewBinding
    private lateinit var mIse: SpeechEvaluator
    init {
        this.context = context
        this.binding = binding
        this.mIse = mIse
        setParams()
    }
    // 评测监听接口
    private val mEvaluatorListener: EvaluatorListener = object : EvaluatorListener {
        override fun onResult(result: EvaluatorResult, isLast: Boolean) {
            Log.d(TAG, "evaluator result :$isLast")
            if (isLast) {
                val builder = StringBuilder()
                builder.append(result.resultString)
                Log.d(TAG, "evaluator result :${builder.toString()}")
                mLastResult = builder.toString()
                val resultParser = XmlResultParser()
                val result = resultParser.parse(mLastResult)
                val gsonStr = Gson().toJson(result)
                Log.e(TAG,gsonStr)
                if (null != result) {
                    binding.webView.loadUrl("javascript:recordFinishCallback('${gsonStr}')")
                } else {
                    ToastUtils.showToast("语音解析失败")
                }
            }
        }

        override fun onError(error: SpeechError) {
            Log.d(TAG, "evaluator over")
        }

        override fun onBeginOfSpeech() {
            // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            Log.d(TAG, "evaluator begin")
            binding.webView.loadUrl("javascript:androidStartRecord()")
        }

        override fun onEndOfSpeech() {
            // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
            Log.d("WebView", "evaluator stoped")
        }

        override fun onVolumeChanged(volume: Int, data: ByteArray) {
            Log.d("WebView","当前音量:$volume")
            Log.d(TAG, "返回音频数据:" + data.size)
        }

        override fun onEvent(eventType: Int, arg1: Int, arg2: Int, obj: Bundle) {
            // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
            //	if (SpeechEvent.EVENT_SESSION_ID == eventType) {
            //		String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
            //		Log.d(TAG, "session id =" + sid);
            //	}
        }
    }

    @JavascriptInterface
    fun goBack() {
        EventBus.getDefault().post("view_goBack")
    }

    // 启动录音
    @JavascriptInterface
    fun startRecord(content:String,timeout:String,vad_bos:String,vad_eos:String) {
        Log.e(TAG,"timeout::$timeout")
        Log.e(TAG,"vad_bos::$vad_bos")
        Log.e(TAG,"vad_eos::$vad_eos")
        if (ContextCompat.checkSelfPermission(this.context, Manifest.permission.RECORD_AUDIO)
            != PackageManager.PERMISSION_GRANTED
        ) {
            // 如果没有录音权限,请求权限
            ActivityCompat.requestPermissions(
                this.context,
                arrayOf(Manifest.permission.RECORD_AUDIO),
                REQUEST_RECORD_AUDIO_PERMISSION
            )
        } else {
            // 已经有录音权限,执行录音操作
            mIse?.setParameter(SpeechConstant.KEY_SPEECH_TIMEOUT, timeout)
            mIse?.setParameter(SpeechConstant.VAD_BOS, vad_bos)// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
            mIse?.setParameter(SpeechConstant.VAD_EOS, vad_eos)// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
            val int = mIse?.startEvaluating(content, null, mEvaluatorListener)
            if (mIse == null){
                Log.e(TAG,"mIse is null")
            }
            Log.e(TAG,"startRecord")
            Log.e(TAG,"ErrorCode:$int")
        }

    }

    // 停止录音
    @JavascriptInterface
    fun stopRecord() {
        Log.e(TAG,"stoping...")
        mIse.stopEvaluating()
    }

    // 评测语种
    private var language: String? = null
    // 评测题型
    private var category: String? = null
    // 结果等级
    private var result_level: String? = null
    private var mLastResult: String? = null
    private fun setParams() {
//        mIse = SpeechEvaluator.createEvaluator(context, null);
        // 设置评测语言
        language = "en_us"
        // 设置需要评测的类型
        category = "read_sentence"
        // 设置结果等级(中文仅支持complete)
        result_level = "complete"
        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        val vad_bos = "5000"
        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        val vad_eos = "1800"
        // 语音输入超时时间,即用户最多可以连续说多长时间;
        val speech_timeout = "5000"

        // 设置流式版本所需参数 : ent sub plev
        if ("zh_cn" == language) {
            mIse?.setParameter("ent", "cn_vip")
        }
        if ("en_us" == language) {
            mIse?.setParameter("ent", "en_vip")
        }
        mIse?.setParameter(SpeechConstant.SUBJECT, "ise")
        mIse?.setParameter("plev", "0")

        // 设置评分百分制 使用 ise_unite  rst  extra_ability 参数, 其中ise_unite==0是5分制,==1是百分制
        mIse?.setParameter("ise_unite", "1")
        mIse?.setParameter("rst", "entirety")
        mIse?.setParameter("extra_ability", "syll_phone_err_msg;pitch;multi_dimension")
        mIse?.setParameter(SpeechConstant.LANGUAGE, language)
        mIse?.setParameter(SpeechConstant.ISE_CATEGORY, category)
        mIse?.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8")
//        mIse?.setParameter(SpeechConstant.VAD_BOS, vad_bos)
//        mIse?.setParameter(SpeechConstant.VAD_EOS, vad_eos)
//        mIse?.setParameter(SpeechConstant.KEY_SPEECH_TIMEOUT, speech_timeout)
        mIse?.setParameter(SpeechConstant.RESULT_LEVEL, result_level)
        mIse?.setParameter(SpeechConstant.AUDIO_FORMAT_AUE, "opus")
        // 设置音频保存路径,保存音频格式支持pcm、wav,
        mIse?.setParameter(SpeechConstant.AUDIO_FORMAT, "wav")
        mIse?.setParameter(
            SpeechConstant.ISE_AUDIO_PATH,
            context.getExternalFilesDir("msc")?.absolutePath + "/ise.wav"
        )
        //通过writeaudio方式直接写入音频时才需要此设置
        //mIse?.setParameter(SpeechConstant.AUDIO_SOURCE,"-1");
    }
}

我来解释一下上面的工具类里的内容,可以看到方法上有一个注解@JavascriptInterface,这个注解是表示是给h5的js代码调用的,如果不涉及和前端js交互的话,这个注解可以删。其中评测最关键的方法,就是setParams方法,这个方法配置了一些打分参数等,注释已经很详细了,然后就是startRecord方法,这个方法就是开始录音的方法,这个方法还动态传入了几个参数,这几个参数是用来配置几个时长的,其中

timeout:String是用来设置最长录制几秒就自动停止录制并开始打分,
vad_bos:String是表示用户多长时间不说话则当做超时处理,
vad_eos:String是表示用户停止说话多长时间内即认为不再输入, 自动停止录音

还有一些配置在setParams方法中的参数,特别说一个分制的参数,有满分百分制和5分制,其中ise_unite==0是5分制,==1是百分制:

        // 设置评分百分制 使用 ise_unite  rst  extra_ability 参数, 其中ise_unite==0是5分制,==1是百分制
        mIse?.setParameter("ise_unite", "1")

然后就是关键的结果处理监听回调mEvaluatorListener了,这个是一个监听对象,这个对象复写了回调方法,来处理讯飞返回的结果,这里用到了一个类库,XmlResultParser,这个类库我们需要从讯飞的sdk中的sample中去copy过来,如图,我们把result文件夹完整复制
在这里插入图片描述
然后粘贴到自己项目包下的位置,如图,此时我们需要把这里面所有文件中引用的错误的包名路径修改成我们自己的正确的包名,然后再去工具类中引用XmlResultParser工具,这个工具,顾名思义,就是讯飞的程序员手撸了一个xml解析工具
在这里插入图片描述
sample自带的这个XmlResultParser工具,里面的实体类的数据不是很全,比如有一些流畅分,准确度分这些都没有,我们可以手动加一下,如图,打开result文件,我加了三个分值数据,在这里加的属性,必须得是人家返回结果包含的,不能随便加:
在这里插入图片描述
result这里加了以后,还需要在这里加一下解析:
在这里插入图片描述
然后怎么调用这个工具类呢?很简单,先在activity中创建一个mIse全局对象,在oncreate中赋值,我这里只写了赋值,声明的代码你自己写吧,然后传入这个对象,至于我用的这个工具对象jsBridge,你完全可以不按照我这个来,你可以直接吧工具类定义成object类型,怎么调用都可以。我是将上下文this传了过去,以防有ui操作,我还将binding传了进去,你们不需要可以不传,然后把mIse评测对象传了进去。

mIse = SpeechEvaluator.createEvaluator(this, null)
jsBridge = JsBridge(this, binding,mIse!!)

如果你觉得麻烦,甚至可以直接将工具类的那部分关键代码复制到你的activity里,省的麻烦,我之所以分离,是因为我的activity里逻辑太多了,看着有些头大。

然后附上我的运行图:
在这里插入图片描述

到此,就算大功告成了,写博客太累了,为了帮助大家理解,我仔细梳理了流程,花了有三个小时吧,要是我个人做记录,可能复制几个代码就够了,所以如果帮到阁下,还望给个关注,虽然关注也不挣钱,但是看到有人跟我有互动,我就知道这个世界,不止我一个人。

写在最后:

以下说一些个人观点,不想看或者不认同的就可以离开了:

关于科大讯飞:讯飞给了每天500的免费评测数量,这个气量,就比某些公司要强很多了,再加上讯飞的技术确实很好,所以基本在同行业市场上没有对手,比如云知声(个人免费半年,企业免费一年,有安卓sdk但是文档说明比葛优的头发还要稀疏),还有一些小众的比如有个叫什么驰声的这种完全没有免费额度的(而且sdk还是你在他们官网留下电话然后他们的销售给你打电话联系你,然后加你微信跟你拉个群让技术给申请一个appkey来使用),当然,讯飞的翻译功能也强于专业做翻译的同行不少,比如有道,有道的翻译比较死板,讯飞没有专攻翻译领域,否则应该就没有有道翻译的市场了。用过讯飞输入法的应该都知道,讯飞输入法自带语音转文字,甚至可以你直接说中文,他转英文文字,而且是多年前就很准确了,甚至他还能识别方言,其实要说国内的AI大模型,我唯一比较期待的就是讯飞的大模型了,关于文心一言和千义通问,我是不怎么看好的,因为这两为了竞争市场匆忙上线,上下文关联性以及对于中文理解能力跟ChatGPT比起来差得不是一点半点,甚至听说文心一言还推出了付费版本,这一点就挺搞笑的,有点搞不清楚自己的斤两,也有可圈可点的地方,那就是免费版支持图片生成,虽然生成的图不尽如人意吧,但是也算一个得分项。

关于浏览器端:因为浏览器对于录音功能的限制,也就只能局限于PC端用一用了,移动端用h5来访问录音功能,体验很不好。要开发移动端使用的,建议还是开发一个app,如果实在想做网页,也可以使用原生壳子加webview嵌套h5来实现讯飞语音的功能,这样可以使用原生的录音功能和讯飞sdk,这样实现起来用户使用很舒服,开发也很舒服。

关于uniapp:还有就是uniapp的客户端版不要接入科大讯飞的所有内容,这是因为科大讯飞已经封禁了新客户的http的请求方式,只能通过websocket的方式进行流式数据传输,而uniapp的录音分片功能不支持app端,只支持部分小程序,而uniapp的h5端就更不考虑了,uni开发是挺舒服的,但是遇到问题了,就知道uniapp还不如原生vue项目好做。其实如果非小程序需求,不建议使用uniapp做开发,除非是特别简单的页面需求,否则涉及一点点特殊定制的硬件功能,可能就歇菜了,举个简单例子,uniapp不支持使用formdata进行数据传输,而他提供了一个叫upload的方法,说是能代替进行formdata 传输,但是格式不能自定义,相当于白搞,再就是从uniapp的文档上就可以看到各种的不支持,有的功能不支持app端,有的不支持h5端,有的不支持小程序端,有的干脆只支持h5,有的只支持微信小程序,有的只支持抖音小程序,
说句唱衰的话,就uni这样的开发平台,你说该咋用,说他是跨平台解决方案弃之可惜吧,但是用着又如此食之无味,这大概也就是他这么多年没火起来的原因吧,给你们截个图你们就大概明白了,这个截图只是冰山一角,
在这里插入图片描述
所以想搞跨平台方案的,建议还是考虑清楚,毕竟貌似uni的团队并不打算维护这个uniapp了,那些社区里陈年的bug,貌似也打算让他继续陈年了,千万别相信uni市场上那些收费的插件,可能各平台版本更新一次插件就歇菜了,听说最近uni出了一个appx,貌似很吊的样子,说是性能和原生一样,众所周知,uniapp是一个webview的性能,性能很差,要是这个appx能做好,确实会很有市场,那将是要超越flutter的存在了,毕竟uni还能兼容小程序,但是要我相信dcloud团队能做好他,诸葛亮来当说客,我也得犹豫一下要不要信,我预估这个appx能用的周期应该是10年左右,但是技术更新换代这么快,10年也许写代码都不用太多的人力了,所以本人暂时先不考虑用了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何用甘特图跟踪项目进度

甘特图是一个简单但是极其强大的项目管理工具,能够清晰可视化复杂项目的进度,在项目跟踪和控制上发挥重要作用。任何一个严肃的项目组织者都会使用甘特图来规划和管理项目中的任务。 甘特图的纵坐标表示项目的各项活动或任务,横坐标表示项目的时间进度。每个任务用一条横条表示…

掌握Pyecharts:绘制炫酷词云图的参数解析与实战技巧【第36篇—python:词云图】

文章目录 安装Pyecharts基本的词云图绘制自定义词云图样式多种词云图合并高级词云图定制与交互1. 添加背景图片2. 添加交互效果 使用自定义字体和颜色从文本文件生成词云图总结&#xff1a; 在数据可视化领域&#xff0c;词云图是一种极具表现力和趣味性的图表&#xff0c;能够…

C++(搜索二叉树)

目录 前言&#xff1a; 1.二叉搜索树 1.1二叉搜索树的定义 1.2二叉搜索树的特点 2.二叉搜索树的实现 2.1框架 2.2查找 2.3插入 2.4删除 1.右子树为空 2.左子树为空 3.左右都不为空 3.递归版本 3.1前序遍历 3.2中序遍历 3.3后续遍历 3.4查找&#xff08;递…

SPA单页面的讲解(超级详细)

目录 一、什么是SPA 二、SPA和MPA的区别 单页应用与多页应用的区别 单页应用优缺点 三、实现一个SPA 原理 实现 hash 模式 history模式 四、题外话&#xff1a;如何给SPA做SEO SSR服务端渲染 静态化 使用Phantomjs针对爬虫处理 一、什么是SPA SPA&#xff08;sin…

Windows打开IE浏览器命令最简单的方法

问题场景&#xff1a; 许多插件或特定版本的系统需要使用ie浏览器来访问&#xff0c;window默认的ie浏览器是被禁用的如何快速打开ie浏览器解决问题 目录 问题场景&#xff1a; 测试环境&#xff1a; 检查环境是否支持&#xff1a; 问题解决&#xff1a; 方法一 方法二 方法…

支持IPv4与IPv6双协议栈的串口服务器,IPv6串口服务器

物联网是啥玩意儿&#xff1f;这是首先要搞明白的。按照百度百科的说法&#xff0c;是将各种信息传感设备&#xff0c;如射频识别&#xff08;RFID&#xff09;装置、红外感应器、全球定位系统、激光扫描器等种种装置与互联网结合起来而形成的一个巨大网络。这个说法有些复杂&a…

【JaveWeb教程】(33)SpringBootWeb案例之《智能学习辅助系统》的详细实现步骤与代码示例(6)修改员工的实现

目录 SpringBootWeb案例063. 修改员工3.1 查询回显3.1.1 接口文档3.1.2 实现思路3.1.3 代码实现3.1.4 postman测试 3.2 修改员工3.2.1 接口文档3.2.2 实现思路3.2.3 代码实现3.2.4 postman测试3.2.5 前后端联调测试 SpringBootWeb案例06 前面我们已经实现了员工信息的条件分页…

linux jenkins相关命令

1.jenkins启动命令 [rootlocalhost /]#service jenkins start 2.jenkins停止命令 [rootlocalhost /]#service jenkins stop 3.查询jenkins状态命令 [rootlocalhost /]#service jenkins status 4.重启jenkins命令 [rootlocalhost /]#service jenkins restart Jenkins默认的端口号…

牵手国际企业,OpenAI计划自己制造AI芯片

据外媒报道称&#xff0c;近日&#xff0c;OpenAI的首席执行官萨姆奥特曼正在积极洽谈一项规模达数十亿美元的投资项目&#xff0c;计划与多家顶级芯片制造商合作&#xff0c;建设一个覆盖全球的人工智能芯片生产网络。 奥特曼曾多次反馈目前的芯片已不能满足OpenAI公司的AI研发…

九、Kotlin 注解

1. 什么是注解 注解是对程序的附件信息说明。 注解可以作用在类、函数、函数参数、属性等上面。 注解的信息可用于源码级、编译期、运行时。 2. 注解类的定义 使用元注解 Retention 声明注解类的作用时期。 使用元注解 Target 声明注解类的作用对象。 定义注解类时可以声…

Linux详细笔记大全

第0章 Linux基础入门 什么是计算机 计算机的组成: 控制器,是整个计算机的中枢神经,根据程序要求进行控制,协调计算机各部分工作及内存与外设的访问等。 运算器,功能是对数据进行各种算术运算和逻辑运算。 存储器,功能是存储程序、数据和各种信号、命令等信息。 输入设备…

vue3-element-admin的组件el-time-picker设置只能选择上午或下午

上午&#xff1a; <el-time-picker style"width: 80%;" :disabled"!top_status" is-range v-model"top_time"range-separator"至" start-placeholder"开始时间" end-placeholder"结束时间" placeholder"…

Spring Security 存储密码之 JDBC

Spring Security的JdbcDaoImpl实现了UserDetailsService接口,通过使用JDBC提供支持基于用户名和密码的身份验证。 JdbcUserDetailsManager扩展了JdbcDaoImpl,通过UserDetailsManager接口提供UserDetails的管理功能。 当Spring Security配置为接受用户名/密码进行身份验证时,…

59.螺旋矩阵II(力扣LeetCode)

59.螺旋矩阵II 题目描述 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 示例 2&#xff1a; 输…

Arduino开发实例-DRV8833电机驱动器控制直流电机

DRV8833电机驱动器控制直流电机 文章目录 DRV8833电机驱动器控制直流电机1、DRV8833电机驱动器介绍2、硬件接线图3、代码实现DRV8833 使用 MOSFET,而不是 BJT。 MOSFET 的压降几乎可以忽略不计,这意味着几乎所有来自电源的电压都会传递到电机。 这就是为什么 DRV8833 不仅比基…

php项目下微信小程序对接实战问题与解决方案

一.实战问题与方案总结 1.SQL查询条件是一组数&#xff0c;传参却是一个字符串导致报错&#xff0c;如下 SQLSTATE[HY093]: Invalid parameter number (SQL: select count(*) as aggregate from car_video where province_id in (1492) and city_id in (1493) and county_id …

GEM5 Garnet Standalone 命令行与stats.txt结果分析

简介 展示了不同的命令行与结果,作为初步的了解. 命令行 sim-cycles要大,不然没结果 ./build/NULL/gem5.debug configs/example/garnet_synth_traffic.py –num-cpus16 –num-dirs16 –networkgarnet –topologyMesh_XY –mesh-rows4 –sim-cycles1000000 --inj-vnet…

php+Layui开发的网站信息探针查询源码

信息探针是一款基于layui开发的专业查询好友个人信息的程序。 自定义设置探针页面&#xff0c;探针功能&#xff0c;QQ分享&#xff0c;通知邮箱等功能。 生成页面链接好友点击会出现好友ip 位置信息&#xff0c;手机型号ua头浏览器等信息 gps需要注册百度地图开发者才可以使用…

找不到msvcp110.dll怎么办,msvcp110.dll丢失修复方法分享

当计算机系统中无法找到msvcp110.dll这个特定的动态链接库文件时&#xff0c;可能会引发一系列运行问题和功能受限的情况。msvcp110.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;对于许多基于Windows的应用程序来说&#xff0c;它是至关重要的运行组件…

数据结构——用链表实现Map

目录 一、映射&#xff08;Map&#xff09; 二、代码实现 1.建立接口 2.方法实现 &#xff08;1&#xff09;映射的建立 键&#xff08;key&#xff09;和值&#xff08;val&#xff09;的建立 重写toString方法 &#xff08;2&#xff09;构造方法 &#xff08;3&…