【Unity入门】热更新框架之xLua

目录

  • 一、xLua概述
    • 1.1xLua简介
    • 1.2xLua安装
  • 二、Lua文件加载
    • 2.1执行字符串
    • 2.2加载Lua文件
    • 2.3自定义loader
  • 三、xLua文件配置
    • 3.1打标签
    • 3.2静态列表
    • 3.3动态列表
  • 四、Lua与C#交互
    • 4.1 C#访问Lua
      • 4.1.1 获取一个全局基本数据类型
      • 4.1.2 访问一个全局的table
      • 4.1.3 访问一个全局的function
    • 4.2 Lua调用C#
      • 4.2.1 new C#对象
      • 4.2.2 访问C#静态属性,方法
      • 4.2.3 访问C#成员属性,方法
      • 4.2.4 访问父类属性,方法
      • 4.2.5 参数的输入输出属性(out,ref)
      • 4.2.6 重载方法
      • 4.2.7 操作符
      • 4.2.8 参数带默认值的方法
      • 4.2.9 可变参数方法
      • 4.2.10 使用Extension methods
      • 4.2.11 泛化(模版)方法
      • 4.2.12 枚举类型
      • 4.2.13 delegate使用(调用,+,-)
      • 4.2.14 event
      • 4.2.15 C#复杂类型和table的自动转换
      • 4.2.16 “强”转
      • 4.2.17 C#与Lua交互原理
  • 五、xLua热更新
    • 5.1 使用方式
    • 5.2 约束
    • 5.3 API
    • 5.4 Hotfix Flag
    • 5.5 使用建议
  • 六、热更新大致流程

一、xLua概述

1.1xLua简介

xLua是由腾讯维护的一个开源项目,xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用。自2016年初推广以来,已经应用于十多款腾讯自研游戏,因其良好性能、易用性、扩展性而广受好评。现在,腾讯已经将xLua开源到GitHub。
git下载地址:https://github.com/Tencent/xLua
xLua教程地址:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua%E6%95%99%E7%A8%8B.md

xLua在功能、性能、易用性都有不少突破,这几方面分别最具代表性的是:

  1. 可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现;
  2. 出色的GC优化,自定义struct,枚举在Lua和C#间传递无C# gc alloc;
  3. 编辑器下无需生成代码,开发更轻量;

除此之外,xLua另一特色功能就是代码热补丁。非常适合前期没有规划使用Lua进行逻辑开发,后期又需要在iOS这种平台获得代码热更新能力的项目。

1.2xLua安装

  1. 首先下载最新版xLua ,然后解压到你想解压到的位置;
    在这里插入图片描述

  2. xLua-master工程打开后,你将会看到一个Assets目录,点击到Assets目录会看到以下几个文件
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/903d2e6a80a24917a0f6d8c4f2ddc595.png
    将 xLua-master 工程中Assets目录下的文件复制到你的Unity工程的Assets目录下即可;
    在这里插入图片描述

  3. 加载完后,菜单栏有 xlua 菜单表示,导入成功
    在这里插入图片描述

  4. 在菜单栏中 xlua 菜单添加 Hotfix Inject In Editor 选项
    在这里插入图片描述
    菜单栏中 xlua 菜单会显示 Hotfix Inject In Editor 选项
    在这里插入图片描述

  5. 如果希望安装到其它目录,可参看:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/faq.md

  6. xLua API可参见链接:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua_API.md

示例代码:

using UnityEngine;
//引入 Xlua 命名空间
using XLua;public class HotFixScript : MonoBehaviour
{//lua 环境变量private LuaEnv luaEnv;private void Start(){//创建lua运行环境luaEnv = new LuaEnv();luaEnv.DoString("print('Hellow World 1')");}private void OnDestroy(){//释放lua环境luaEnv.Dispose();}
}

编写完脚本,挂载到游戏物体上,先按 Generate Code 选项生成代码,再按 Hotfix Inject In Editor 选项将补丁注入编辑器,运行结果:
在这里插入图片描述

二、Lua文件加载

2.1执行字符串

最基本是直接用LuaEnv.DoString执行一个字符串,当然,字符串得符合Lua语法

比如:luaenv.DoString(“print(‘hello world’)”)

完整代码见XLua\Tutorial\LoadLuaScript\ByString目录。

但这种方式并不建议,更建议下面介绍这种方法加载Lua文件。

2.2加载Lua文件

用lua的require函数即可,比如:DoString(“require ‘byfile’")

require实际上是调一个个的loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。

完整代码见XLua\Tutorial\LoadLuaScript\ByFile目录。

2.3自定义loader

在xLua加自定义loader是很简单的,只涉及到一个接口:

public delegate byte[] CustomLoader(ref string filepath);

public void LuaEnv.AddLoader(CustomLoader loader);

通过AddLoader可以注册个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。该回调返回值是一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。

有了这个就简单了,用IIPS的IFS?没问题。写个loader调用IIPS的接口读文件内容即可。文件已经加密?没问题,自己写loader读取文件解密后返回即可。
示例代码:

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using XLua;public class LuaStudy : MonoBehaviour
{private LuaEnv luaEnv;private void Start(){//创建lua运行环境luaEnv = new LuaEnv();LuaLoaderFun();VisitLua_Table();VisitLua_Function();}private void OnDestroy(){//释放lua环境luaEnv.Dispose();}/// <summary>/// 加载及调用lua脚本/// </summary>void LuaLoaderFun(){//执行lua的输出语句luaEnv.DoString("print('Hellow World 1')");//在lua中调用C#方法luaEnv.DoString("CS.UnityEngine.Debug.Log('Hellow World 2')");//加载运行Resources文件中的lua脚本TextAsset ta = Resources.Load<TextAsset>("HellowWorld.lua");luaEnv.DoString(ta.text);//加载运行helloworld.lua.txt  默认从Resources文件中进行加载luaEnv.DoString("require 'HellowWorld'");// 添加自定义的Loader方法//添加了一个自定义的Loader返回null并且DoString里面添加的是一个不存在的lua文件。它会返回错误。//luaEnv.AddLoader(MyLoader_1);//luaEnv.DoString("require 'xxx'");// 添加自定义的Loader方法//添加了一个自定义的Loader返回lua语句的二进制并且DoString里面添加的是一个不存在的lua文件。它会执行自定义的Loader的输出。//luaEnv.AddLoader(MyLoader_2);//luaEnv.DoString("require 'xxx'");//通过自定义Loader加载指定目录的Lua脚本luaEnv.AddLoader(MyLoader_3);luaEnv.DoString("require 'Lua_StreamingAssets'");}/// <summary>/// 访问获取lua属性和表/// </summary>void VisitLua_Table() {//调用lua脚本才能访问属性luaEnv.DoString("require 'LuaTest'");//获取lua脚本中的全局变量int num = luaEnv.Global.Get<int>("num");//获取lua里面的全局变量numstring name = luaEnv.Global.Get<string>("name");//获取lua里面的全局变量nameDebug.Log("num:" + num + "  name:" + name);//访问lua中的table(映射到class)Person personTemp = luaEnv.Global.Get<Person>("person");Debug.Log(personTemp.name + "    " + personTemp.age);//这种方式的访问,修改age的值不会影响到lua里面的表的属性personTemp.name = "wang";personTemp.age = 50;//修改后试一下Person personTemp2 = luaEnv.Global.Get<Person>("person");Debug.Log(personTemp2.name + "    " + personTemp2.age);//访问lua中的table(映射到interface)//映射到interface修改p中的属性,lua中的原table也会发生变化IPerson personTemp3 = luaEnv.Global.Get<IPerson>("person");Debug.Log(personTemp2.name + "    " + personTemp2.age);//通过LuaTable访问tableList<object> list = luaEnv.Global.Get<List<object>>("person");foreach (object o in list){print(o);}//通过LuaTable类 比较慢LuaTable luaTable = luaEnv.Global.Get<LuaTable>("person");print(luaTable.Get<string>("name"));print(luaTable.Get<int>("age"));}/// <summary>/// 访问lua中的全局方法/// </summary>void VisitLua_Function(){//调用lua脚本才能访问属性luaEnv.DoString("require 'LuaTest'");//使用委托来获取Lua中的全局函数Add add = luaEnv.Global.Get<Add>("add");add(2, 4);//通过LuaFunction访问Lua中的全局函数LuaFunction luaFunction = luaEnv.Global.Get<LuaFunction>("add");luaFunction.Call(2, 4);}private byte[] MyLoader_1(ref string filePath){print(filePath);return null;}private byte[] MyLoader_2(ref string filePath) {string s = "print(123)";return System.Text.Encoding.UTF8.GetBytes(s);}private byte[] MyLoader_3(ref string filePath) {string path = Application.streamingAssetsPath + "/" + filePath + ".lua.txt";return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path));}class Person{public string name;public int age;}//打标签[CSharpCallLua]public interface IPerson{string name { get; set; }int age { get; set; }}//打标签[CSharpCallLua]delegate void Add(int a, int b);}

完整示例见XLua\Tutorial\LoadLuaScript\Loader。

三、xLua文件配置

xLua所有的配置都支持三种方式:打标签;静态列表;动态列表。
配置有两必须两建议:

  • 列表方式均必须是静态的字段/属性
  • 列表方式均必须放置一个静态类
  • 建议使用标签方式
  • 建议列表方式配置放Editor目录(如果是Hotfix配置, - - 并且类位于Assembly-CSharp.dll之外的其他dll,必须放Editor目录)

3.1打标签

xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置,比如你想从lua

调用c#的某个类,希望生成适配代码,你可以为这个类型打一个LuaCallCSharp标签:

[LuaCallCSharp]
publicclassA
{}

该方式方便,但在il2cpp下会增加不少的代码量,不建议使用。

常用标签如下所示:

  1. [XLua.LuaCallCSharp]:为一个C#类型加了这个配置,xLua会生成这个类型的适配代码(否则将会尝试用性能较低的反射方式来访问),提供给Lua调用。

  2. [XLua.CSharpCallLua]:如果希望Lua文件中的逻辑能够被C#调用,可为Lua重的函数等打上该标签。

  3. [XLua.Hotfix]:用于标记需要进行热更新的文件。

除此之外,xLua还提供了其他标签,具体可参见链接:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/configure.md

3.2静态列表

有时我们无法直接给一个类型打标签,比如系统api,没有源码的库,或者实例化的泛化类型,那么你可以在一个静态类里声明一个静态字段,该字段的类型除BlackList和AdditionalProperties之外只要实现了IEnumerable就可以了(这两个例外后面具体会说),然后为这字段加上标签:

[LuaCallCSharp]
public static List<Type> mymodule_lua_call_cs_list = new List<Type>()
{typeof(GameObject),typeof(Dictionary<string, int>),
};

该字段需要放置一个静态类里头,建议放置Editor目录。

3.3动态列表

声明一个静态属性,打上相应的标签即可。

[Hotfix]
public static List<Type> by_property
{get{return (from type in Assembly.Load("Assembly-CSharp").GetTypes()where type.Namespace == "XXXX"select type).ToList();}
}

Getter是代码,你可以实现很多效果,比如按名称空间配置,按程序集配置等等。

该属性需要放置一个静态类里头,建议放置Editor目录。

四、Lua与C#交互

4.1 C#访问Lua

4.1.1 获取一个全局基本数据类型

访问LuaEnv.Global就可以了,上面有个模版Get方法,可指定返回的类型。

luaenv.Global.Get<int>("a")
luaenv.Global.Get<string>("b")
luaenv.Global.Get<bool>("c")

4.1.2 访问一个全局的table

1、映射到普通class或struct

定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{f1 = 100, f2 = 100}可以定义一个包含public int f1;public int f2;的class。这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。

要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。这个功能可以通过把类型加到GCOptimize生成降低开销。

2、映射到一个interface

这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。

3、更轻量级的by value方式:映射到Dictionary<>,List<>

不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。

4、另外一种by ref方式:映射到LuaTable类

这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。

4.1.3 访问一个全局的function

1、映射到delegate

这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。

delegate要怎样声明呢?

对于function的每个参数就声明一个输入类型的参数。

多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。

参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。

delegate的使用就更简单了,直接像个函数那样用就可以了。

2、映射到LuaFunction

这种方式的优缺点刚好和第一种相反。

使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。

4.2 Lua调用C#

4.2.1 new C#对象

在C#这样new一个对象:

var newGameObj = new UnityEngine.GameObject();

对应到Lua是这样:

local newGameObj = CS.UnityEngine.GameObject()

基本类似,除了:

lua里头没有new关键字;

所有C#相关的都放到CS下,包括构造函数,静态成员属性、方法;

如果有多个构造函数呢?放心,xlua支持重载,比如你要调用GameObject的带一个string参数的构造函数,这么写:

local newGameObj2 = CS.UnityEngine.GameObject('helloworld')

4.2.2 访问C#静态属性,方法

读静态属性

CS.UnityEngine.Time.deltaTime

写静态属性

CS.UnityEngine.Time.timeScale = 0.5

调用静态方法

CS.UnityEngine.GameObject.Find('helloworld')

小技巧:如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能:

local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')

4.2.3 访问C#成员属性,方法

读成员属性

testobj.DMF

写成员属性

testobj.DMF = 1024

调用成员方法

注意:调用成员方法,第一个参数需要传该对象,建议用冒号语法糖,如下

testobj:DMFunc()

4.2.4 访问父类属性,方法

xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基

类的成员属性,成员方法。

4.2.5 参数的输入输出属性(out,ref)

Lua调用测的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参

out修饰的不算,然后从左往右对应lua 调用测的实参列表;

Lua调用测的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。

4.2.6 重载方法

直接通过不同的参数类型进行重载函数的访问,例如:

testobj:TestFunc(100)

testobj:TestFunc(‘hello’)

将分别访问整数参数的TestFunc和字符串参数的TestFunc。

注意:xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一

对多的情况,比如C#的int,float,double都对应于lua的number,上面的例子中

TestFunc如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码

中排前面的那个)。

4.2.7 操作符

支持的操作符有:+,-,*,/,==,一元 -,<,<=, %,[]

4.2.8 参数带默认值的方法

和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。

4.2.9 可变参数方法

对于C#的如下方法:

void VariableParamsFunc(int a, params string[] strs)

可以在lua里头这样调用:

testobj:VariableParamsFunc(5, 'hello', 'john')

4.2.10 使用Extension methods

在C#里定义了,lua里就能直接使用。

4.2.11 泛化(模版)方法

不直接支持,可以通过Extension methods功能进行封装后调用。

4.2.12 枚举类型

枚举值就像枚举类型下的静态属性一样。

testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)

上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的

另外,如果枚举类加入到生成代码的话,枚举类将支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换,例如:

CS.Tutorial.TestEnum.__CastFrom(1)

CS.Tutorial.TestEnum.__CastFrom(‘E1’)

4.2.13 delegate使用(调用,+,-)

C#的delegate调用:和调用普通lua函数一样

+操作符:对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的

C#delegate或者是lua函数。

-操作符:和+相反,把一个delegate从调用链中移除。

Ps:delegate属性可以用一个luafunction来赋值。

4.2.14 event

比如testobj里头有个事件定义是这样:public event Action TestEvent;

增加事件回调

testobj:TestEvent(‘+’, lua_event_callback)

移除事件回调

testobj:TestEvent(‘-’, lua_event_callback)

4.2.15 C#复杂类型和table的自动转换

对于一个有无参构造函数的C#复杂类型,在lua侧可以直接用一个table来代替,该table对应复杂类型的public字段有相应字段即可,支持函数参数传递,属性赋值等,例如:

C#下B结构体(class也支持)定义如下:

public struct A{ public int a;}

public struct B{ public A b; public double c;}

某个类有成员函数如下:void Foo(B b)

在lua可以这么调用:

obj:Foo({b = {a = 100}, c = 200})

获取类型(相当于C#的typeof)

比如要获取UnityEngine.ParticleSystem类的Type信息,可以这样

typeof(CS.UnityEngine.ParticleSystem)。

4.2.16 “强”转

lua没类型,所以不会有强类型语言的“强转”,但有个有点像的东西:告诉xlua要用指定的生成代码去调用一个对象,这在什么情况下能用到呢?有的时候第三方库对外暴露的是一个interface或者抽象类,实现类是隐藏的,这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用该生成代码来访问:

cast(calc, typeof(CS.Tutorial.Calc))

上面就是指定用CS.Tutorial.Calc的生成代码来访问calc对象。

4.2.17 C#与Lua交互原理

1、C#调用Lua:C#先调用Lua的dll文件(C语言写的库),dll文件执行Lua代码

2、Lua调用C#:

1、Wrap方式:非反射机制,需要为源文件生成相应的wrap文件,当启动

Lua虚拟机时,Wrap文件将会被自动注册到Lua虚拟机中,之后,Lua文件

将能够识别和调用C#源文件。

总结:Lua调用Wrap文件, Wrap文件调用C#源文件

2、反射机制

五、xLua热更新

5.1 使用方式

  1. 添加HOTFIX_ENABLE宏打开该特性(在Unity3D的File->Build Setting->Scripting Define Symbols下添加)。编辑器、各手机平台这个宏要分别设置!如果是自动化打包,要注意在代码里头用API设置的宏是不生效的,需要在编辑器设置。(建议平时开发业务代码不打开HOTFIX_ENABLE,只在build手机版本或者要在编译器下开发补丁时打开HOTFIX_ENABLE)

  2. 执行XLua/Generate Code菜单。

  3. 注入,构建手机包这个步骤会在构建时自动进行,编辑器下开发补丁需要手动执行"XLua/Hotfix Inject In Editor"菜单。注入成功会打印“hotfix inject finish!”或者“had injected!”。

5.2 约束

不支持静态构造函数,目前只支持Assets下代码的热补丁,不支持引擎,c#系统库的热补丁。

5.3 API

  1. xlua.hotfix(class, [method_name], fix)
    描述 : 注入lua补丁
  • class : C#类,两种表示方法,CS.Namespace.TypeName或者字符串方式"Namespace.TypeName",字符串格式和C#的Type.GetType要求一致,如果是内嵌类型(Nested Type)是非Public类型的话,只能用字符串方式表示"Namespace.TypeName+NestedTypeName";
  • method_name : 方法名,可选;
  • fix : 如果传了method_name,fix将会是一个function,否则通过table提供一组函数。table的组织按key是method_name,value是function的方式。

base(csobj)
描述 : 子类override函数通过base调用父类实现。
csobj : 对象
返回值 : 新对象,可以通过该对象base上的方法

  1. util.hotfix_ex(class, method_name, fix)
    描述 : xlua.hotfix的增强版本,可以在fix函数里头执行原来的函数,缺点是fix的执行会略慢。
  • method_name : 方法名;
  • fix : 用来替换C#方法的lua function。

5.4 Hotfix Flag

Hotfix标签可以设置一些标志位对生成代码及插桩定制化。

Stateless、Stateful
遗留设置,Stateful方式在新版本已经删除,因为这种方式可以用xlua.util.hotfix_state接口达到类似的效果。

ValueTypeBoxing
值类型的适配delegate会收敛到object,好处是代码量更少,不好的是值类型会产生boxing及gc,适用于对text段敏感的业务。

IgnoreProperty
不对属性注入及生成适配代码,一般而言,大多数属性的实现都很简单,出错几率比较小,建议不注入。

IgnoreNotPublic
不对非public的方法注入及生成适配代码。除了像MonoBehaviour那种会被反射调用的私有方法必须得注入,其它仅被本类调用的非public方法可以不注入,只不过修复时会工作量稍大,所有引用到这个函数的public方法都要重写。

Inline
不生成适配delegate,直接在函数体注入处理代码。

IntKey
不生成静态字段,而是把所有注入点放到一个数组集中管理。

5.5 使用建议

对所有较大可能变动的类型加上Hotfix标识;
建议用反射找出所有函数参数、字段、属性、事件涉及的delegate类型,标注CSharpCallLua;
业务代码、引擎API、系统API,需要在Lua补丁里头高性能访问的类型,加上
LuaCallCSharp

引擎API、系统API可能被代码剪裁调(C#无引用的地方都会被剪裁),如果觉得可能会
新增C#代码之外的API调用,这些API所在的类型要么加LuaCallCSharp,要么ReflectionUse;

六、热更新大致流程

  1. 游戏一开始启动时,检查远程服务器上是否有新的myHotfix.lua文件(例如:md5比对,自己加解密),有的话,下载下来,放在指定目录,没有的话,读取本地已有的myHotfix.lua文件,若文件不存在,则说明不需要热修复,一般这种情况是在项目刚发布的早期,没有进行打补丁;
  2. 项目发布版本前,需要在CustomGenConfig.cs中 加入需要添加[Hotfix]标签的类型,想要更灵活使用的话,可以自己写个配置表,读取配置中的类型,自动添加,***只有加入[Hotfix]标签的类型,才可以调用xlua.hotfix()。

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

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

相关文章

STM32 ESP8266 物联网智能温室大棚 源码PCB原理图 设计文档

资料下载: https://download.csdn.net/download/vvoennvv/88680924 一、概述 本系统以STM32F103C8T6单片机为主控芯片&#xff0c;采用相关传感器构建系统硬件电路。其中使用DHT11温湿度传感器对温度和湿度的采集&#xff0c;MQ-7一氧化碳传感器检测CO浓度&#xff0c;GP2Y101…

Hive集群出现报错信息解决办法

一、报错信息&#xff1a;hive> show databases;FAILED: HiveException java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient 解决办法&#xff1a;1.删除mysql中的元数据库&#xff08;metastore&#xff0…

车载电子电器架构 —— 电子电气系统开发角色定义

车载电子电器架构 —— 电子电气系统开发角色定义 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 注:本文12000字,深度思考者进!!! 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的…

Spark编程实验四:Spark Streaming编程

目录 一、目的与要求 二、实验内容 三、实验步骤 1、利用Spark Streaming对三种类型的基本数据源的数据进行处理 2、利用Spark Streaming对Kafka高级数据源的数据进行处理 3、完成DStream的两种有状态转换操作 4、把DStream的数据输出保存到文本文件或MySQL数据库中 四…

什么是检索增强生成?

检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09;是指对大型语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;输出进行优化&#xff0c;使其能够在生成响应之前引用训练数据来源之外的权威知识库。LLM 用海量数据进行…

win11出现安全中心空白和IT管理员已限制对某些区域的访问(不一样的解决方式),真实的个人经历,并且解决经过

1、个人的产生问题的经历 2023年12月22日&#xff0c;由于我买了一块电脑的固态硬盘1T&#xff0c;想要扩容&#xff0c;原来电脑自带512G(由于个人是一个程序员&#xff0c;导致512G实在太古鸡肋)装好以后&#xff0c;想要重装一下系统&#xff0c;来个大清理。结果不出意料&…

DrGraph原理示教 - OpenCV 4 功能 - 颜色空间

前言 前段时间&#xff0c;甲方提出明确需求&#xff0c;让把软件国产化。稍微研究了一下&#xff0c;那就转QT开发&#xff0c;顺便把以前的功能代码重写一遍。 至于在Ubuntu下折腾QT、OpenCV安装事宜&#xff0c;网上文章很多&#xff0c;照猫画虎即可。 这个过程&#xff0…

据报道,微软的下一代 Surface 笔记本电脑将是其首款真正的“人工智能 PC”

明年&#xff0c;微软计划推出 Surface Laptop 6和 Surface Pro 10&#xff0c;这两款设备将提供 Arm 和 Intel 两种处理器选项。不愿意透露姓名的不透露姓名人士透露&#xff0c;这些新设备将引入先进的人工智能功能&#xff0c;包括配备下一代神经处理单元 (NPU)。据悉&#…

MCS-51单片机的中断源

目录 MCS-51中断源&#xff1a; 中断控制&#xff1a; 1.定时控制寄存器&#xff08;TCON&#xff09; 2.串行口控制寄存器&#xff08;SCON&#xff09; 3.中断允许寄存器(IE) 4.中断优先级控制寄存器(IP) 中断处理: 中断采样&#xff1a; 中断查询&#xff1a; 中断…

.FileZilla的使用和主动模式被动模式介绍

FileZilla的使用和主动模式被动模式介绍 1.FileZilla的使用和主动模式被动模式介绍1.安装下载2.新建组和用户2.1打开后出现如下界面2.2点击编辑打开组这个选项2.3点击添加组以后&#xff0c;点击确认2.4输入组的名称&#xff0c;列如我输入的niyin2.5点击用户选项2.6像上面一样…

【Vue2+3入门到实战】(15)VUE路由入门声明式导航的基本使用与详细代码示例

目录 一、声明式导航-导航链接1.需求2.解决方案3.通过router-link自带的两个样式进行高亮4.总结 二、声明式导航-两个类名1.router-link-active2.router-link-exact-active3.在地址栏中输入二级路由查看类名的添加4.总结 三、声明式导航-自定义类名&#xff08;了解&#xff09…

中间人攻击是什么,会产生哪些危害,如何有效防止中间人攻击

简介 中间人攻击&#xff08;Man-in-the-Middle Attack&#xff0c;简称MITM攻击&#xff09;是一种网络攻击&#xff0c;其原理是攻击者通过各种技术手段将受攻击者控制的一台计算机虚拟放置在网络连接中的两台通信计算机之间&#xff0c;这台计算机称为“中间人”。在攻击过…

【设计模式】状态模式

文章目录 引例状态模式理论状态模式代码优化结合享元模式并发问题解决 策略模式 VS 状态模式 引例 交通信号灯系统的设计与实现 方案一 传统设计方案 定义交通灯颜色的枚举 public enum LightColor { Green,Red,Yellow }交通灯类TrafficLight&#xff0c;处理颜色转换等业务…

安全配置审计概念、应用场景、常用基线及扫描工具

软件安装完成后都会有默认的配置&#xff0c;但默认配置仅保证了服务正常运行&#xff0c;却很少考虑到安全防护问题&#xff0c;攻击者往往利用这些默认配置产生的脆弱点发起攻击。虽然安全人员已经意识到正确配置软件的重要性&#xff0c;但面对复杂的业务系统和网络结构、网…

【数学建模美赛M奖速成系列】Matplotlib绘图技巧(一)

Matplotlib图像基础 写在前面1 基本绘图实例&#xff1a;sin、cos函数图2 plot()函数详解**kwargs参数&#xff1a; 3 matplotlib中绘图的默认配置4 设置图的横纵坐标的上下界5 设置横纵坐标上的记号6 调整图像的脊柱7 添加图例8 给一些特殊点加注释9 子图最后 写在前面 前面我…

CentOS7安装部署Zookeeper

文章目录 CentOS7安装部署Zookeeper一、前言1.简介2.架构3.集群角色4.特点5.环境 二、正文1.部署服务器2.基础环境1&#xff09;主机名2&#xff09;Hosts文件3&#xff09;关闭防火墙4&#xff09;JDK 安装部署 3.单机部署1&#xff09;下载和解压2&#xff09;配置文件3&…

第一讲:BeanFactory和ApplicationContext

BeanFactory和ApplicationContext 什么是BeanFactory 它是ApplicationContext的父接口它才是Spring的核心容器&#xff0c;主要的ApplicationContext实现都组合了它的功能 BeanFactory能做什么? 表面上看BeanFactory的主要方法只有getBean()&#xff0c;实际上控制反转、基…

路由器IP地址及直连实验

实验目的&#xff1a; &#xff08;1&#xff09;理解IP地址&#xff1b; &#xff08;2&#xff09;掌握路由器端口IP地址的配置方法&#xff1b; &#xff08;3&#xff09;理解路由器的直连网络。 实验器材&#xff1a; Cisco packet 实验内容&#xff1a; 实验步骤&a…

【ArcGIS微课1000例】0083:地震灾害图件制作之土壤类型分布图

本文基于1:400万矢量土壤图,制作甘肃积石山6.2级地震100km范围内土壤类型分布图。 文章目录 一、土壤分布图预览二、数据集来源及简介三、土壤分布图制作一、土壤分布图预览 二、数据集来源及简介 1. 数据来源 数据集为1:400万中国土壤图,1:400万中国土壤图(2000)由中国科…

大创项目推荐 深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv

文章目录 0 前言1 课题背景2 实现效果3 DeepSORT车辆跟踪3.1 Deep SORT多目标跟踪算法3.2 算法流程 4 YOLOV5算法4.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…