介绍 Go 的 strings 包有几个函数可用于 [string 数据类型]。这些函数可以轻松地修改和操作字符串。我们可以将函数视为对代码元素执行的操作。内置函数是那些在 Go 编程语言中定义并且可供我们随时使用的函数。
在本教程中,我们将回顾几个可用于在 Go 中处理字符串的不同函数。
字符串大写和小写 函数 strings.ToUpper 和 strings.ToLower 将返回一个字符串,其中原始字符串的所有字母都转换为大写或小写字母。因为字符串是不可变的数据类型,所以返回的字符串将是一个新字符串。字符串中的任何非字母字符都不会更改。
要将字符串 "Sammy Shark" 转换为全大写,你可以使用 strings.ToUpper 函数:
ss := "Sammy Shark" fmt.Println(strings.ToUpper(ss)) Output SAMMY SHARK 要转换为小写:
fmt.Println(strings.ToLower(ss)) Output sammy shark 由于你使用的是 strings 包,因此首先需要将其导入程序中。要将字符串转换为大写和小写,整个程序如下:
package main import ( "fmt" "strings" ) func main() { ss := "Sammy Shark" fmt.Println(strings.ToUpper(ss)) fmt.Println(strings.ToLower(ss)) } strings.ToUpper 和 strings.ToLower 函数使大小写始终保持一致,更容易评估和比较字符串。例如,如果用户的姓名全小写,我们仍然可以通过检查全大写版本来确定他们的姓名是否在我们的数据库中。
字符串搜索函数 strings 包有许多函数可以帮助确定字符串是否包含特定的字符。
函数用法strings.HasPrefix从头开始搜索字符串strings.HasSuffix从末尾开始搜索字符串strings.Contains搜索字符串中的任何位置strings.Count计算字符串出现的次数 strings.HasPrefix 和 strings.HasSuffix 允许你检查字符串是否以特定字符集开头或结尾。
文章目录 叨叨程序入口加载load流程简述Bootstrap.load()Catalina.load(params)init()模板方法init流程总结 start()模板方法流程简述start()模板方法Host.start()特别说明 叨叨 讲真,最开始源码调试几遍下来还是很懵了,尤其是组件初始化过程中不断去调用其他组件的init()方法,中间使用模板方法的设计模式,最终抽象方法交给哪个子类去实现有点绕。这个搞明白之后,start()方法的流程就和init()类似了。过程中涉及的组件经过初步梳理,再结合server.xml中的标签,基本可以猜个七七八八,最后结合Tomcat的架构设计图进行梳理修正。
这里先记录下启动流程的学习。Tomcat源码的下载及相关配置就不赘述了,网上有很多教程。
程序入口 java程序的执行入口是main()方法,根据官网的启动流程描述或者启动脚本中的内容,可以定位到一个类:
org.apache.catalina.startup.Bootstrap
进到这个类中直接搜索main()方法,看下main()方法主要分为两大块:
初始化bootstrap
进入init()方法可以看到是在做类加载器的初始化与设置加载与启动bootstrap
条件判断进入load()和start()的执行,按照猜想,在设置完类加载器后,框架启动的尿性就是读取并加载配置,初始化核心组件对象,然后启动框架。下面着重看下load和start两个方法。 加载load 流程简述 加载什么呢?无非就是:
配置信息的加载(毕竟我们还有个配置文件server.xml)核心对象的加载 看下源码究竟做了什么:
Bootstrap.load() 通过反射调用Catalina.load()
Method method = catalinaDaemon.getClass().getMethod("load", paramTypes); if (log.isDebugEnabled()) log.debug("Calling startup class " + method); System.out.println("******************* >>> 反射调用Catalina.load()"); method.invoke(catalinaDaemon, param); Catalina.load(params) 首次看到load方法,直观感受:
有几个init开头的方法,初始化可能会用到的资源创建了一个Digester对象,并解析输入源 digester.parse(inputSource)执行getServer().init() // inputSource来自于一个文件 digester.parse(inputSource); // 文件正是 configFile = "conf/server.xml"; file = configFile(); 所以digester对象是用来解析server.xml的。
getServer()返回一个Server对象,server何时被set进Catalina对象中的?
最后调用server.init()开始初始化流程。
init()模板方法 init()方法是接口Lifecycle中的方法,可以看到init()方法在LifecycleBase类中实现,该类是一个抽象类,并且在init()方法的执行逻辑中调用抽象方法initInternal(),这不是妥妥的模板方法设计模式么。
所以这个initInternal应该是在某个server实现类A中执行的,且类A需要继承LifecycleBase或其子类。server只有一个实现类 StandardServer 且满足上述条件。
LifecycleBase中init模板方法:
StandardServer中的实现:
// The set of Services associated with this Server.
文章目录 Kafka 的存储日志日志的观察模式日志写入模式日志读写模式删除消息 数据挤压问题数据清理日志删除日志压缩 在Kafka当中数据是以日志的形式存在的
Kafka 的存储日志 在Kafka当中。数据在磁盘当中的存储:
Kafka中的数据是保存在 /export/server/kafka_2.12-2.4.1/data中消息是保存在以:「主题名-分区ID」的文件夹中的数据文件夹中包含以下内容 这些分别对应:
文件名说明00000000000000000000.index索引文件,根据offset查找数据就是通过该索引文件来操作的00000000000000000000.log日志数据文件00000000000000000000.timeindex时间索引leader-epoch-checkpoint持久化每个partition leader对应的 LEO(log end offset、日志文件中下一条待写入消息的offset ) 每个日志文件的文件名为起始偏移量,因为每个分区的起始偏移量是0,所以,分区的日志文件都以0000000000000000000.log开始的默认的每个日志文件最大为:log.segment.bytes =102410241024 是为1G为了简化根据 offset 查找消息,Kafka 日志文件名设计为开始的偏移量。 日志的观察模式 为了方便测试观察,新创建一个topic:「test_10m」,该topic每个日志数据文件最大为10M
bin/kafka-topics.sh --create --zookeeper node1.itcast.cn --topic test_10m --replication-factor 2 --partitions 3 --config segment.bytes=10485760 使用之前的生产者程序以往主题中生产数据,可以观察到如下:
每个log文件的大小最大为10M
日志写入模式 新的消息总是写入到最后的一个日志文件中该文件如果到达指定的大小(默认为:1GB)时,将滚动到一个新的文件中 日志读写模式 根据offset首先需要找到存储数据的 segment 段(注意:offset指定分区的全局偏移量)然后根据这个全局分区 offset 找到相对于文件的 segment段offset最后再根据 「segment段offset」读取消息为了提高查询效率,每个文件都会维护对应的范围内存,查找的时候就是使用简单的二分查找 删除消息 在Kafka中,消息是会被定期清理的。一次删除一个segment段的日志文件。Kafka 的日志管理器,会根据Kafka的配置,来决定哪些文件可以被删除 数据挤压问题 Kafka 消费者消费数据的速度是非常快的,但如果由于处理 Kafka 消息时,由于有一些外部 IO、或者是产生网络拥堵,就会造成 Kafka 中的数据积压(或称为数据堆积)。如果数据一直积压,会导致数据出来的实时性受到较大影响。
当Kafka出现数据积压问题时,首先要找到数据积压的原因。
以下是在企业中出现数据积压的几个类场景:
数据写入MySQL失败 问题描述:
某日运维人员找到开发人员,说某个 topic 的一个分区发生数据积压,这个topic 非常重要,而且开始有用户投诉。运维非常紧张,赶紧重启了这台机器。重启之后,还是无济于事。
问题分析:
一、如何生成IBIS模型,IBIS模型里包括什么 • 1.可以通过仿真过程中或基准测量中收集的数据来获得IBIS 模型。如果选择前一种方法,可以使用SPICE进行仿真, 收集每个输出/输出缓冲器的V/I和V/T数据。这样可以在模 型中包含过程转折数据。然后,使用IBIS网站上的SPICE至 IBIS转换程序可以由SPICE生成IBIS模型。
• 2.模型可以在三种不同条件下生成:典型、最小和最大。在 典型模型中,使用标称电源电压、温度和工艺参数获取数 据;在最小模型中,使用最低电源电压、较高温度和较弱 工艺参数获取数据;对于最大模型,条件是最高电源电 压、较低温度和较强的工艺参数。
• 3.每种条件会产生相应的典型、慢速和快速模型。快速模型 是在具有快速转换时间和最小封装特性的最高电流值条件 下生成的。另一方面,具有较慢转换时间和最大封装值的 最低电流值条件将生成慢速模型。
• 4.如果数据是在实验室测量中获得的,那么模型取决于器件的特性。如果是标称器件,将获得典型模型。 数据收集好后,以可读的ASCII文本格式存入文件中。 为ibischeck,用于根据标准检查IBIS文件的句法和结构。最后一步,设计人员应通过将仿真结果 与实际芯片测量关联起来验证模型。 二、IBIS模型结构,输入和输出模型 IBIS规范支持几种输入和输出,例如可建模为三态、集电极开路、开漏、I/O和ECL的输入/输出。第一步是识别器 件上不同类型的输入和输出,确定设计中存在多少缓冲 器。值得注意的是在IBIS文件中一个模型可用于表示多个 输入或输出。然而,如果C_Comp和封装参数不同,就需要不同的模型。所示为三态输出的结构;模型可视为一个驱动器。它 包含一个PMOS晶体管和一个NMOS晶体管,两个ESD保护 二极管,芯片电容和封装寄生电容。 三、 IBIS模型结构,输出模型,上拉和下拉曲线 • 1.三态输出的结构;模型可视为一个驱动器。它 包含一个PMOS晶体管和一个NMOS晶体管,两个ESD保护 二极管,芯片电容和封装寄生电容。
• 2.输出模型通过以下直流电气数据、交流或转换数据以及参 数进行表征: 1. 上拉和下拉曲线 2. 电源和GND箝位曲线 3. 斜坡速率 4. 上升和下降波形 5. C_Comp 6. 封装参数;
• 3.上拉和下拉曲线,上拉和下拉数据决定器件的驱动强度。这些曲线通过特征 化输出中的两个晶体管来获得。上拉数据描述当输出为逻 辑高电平状态(PMOS晶体管导通)时的I/V行为。反之,下 拉数据表示当输出为逻辑低电平状态(NMOS晶体管导通) 时的直流电气特性。
• 4.数据需要在–VDD至2 × VDD的范围内获取。虽然这个电压范 围超过了半导体厂商在器件规格中指出的绝对最大额定 值,但是这个范围覆盖了传输线中可能发生的欠冲、过冲 和反射的情况。因此,驱动器和接收器需要使用这个电压 范围建模。
四、IBIS模型结构,输出模型,电源和GND箝位曲线
• 1.这些曲线是在输出为高阻态时生成的。GND和电源箝位数 据表示输出端在GND箝位和电源箝位二极管分别导通时的 电气性能。
最近在重构项目时,把java.util.Date类型改成了java.time.LocalDateTime,但是遇到一点小问题,在这里分享一下。
后端代码:
@Data public class RoleChengwei { @Id private String id; /** * 是否在使用 */ private Integer state; /** * 角色id */ private String roleId; /** * 称谓id */ private Integer chengweiId; /** * 装饰过期时间 */ @DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private LocalDateTime endTime; } 前端代码:
$("#end_time").datebox({ width: 160, showSeconds: true, required: true }); 前端界面截图如下
前端使用的是easyui框架,datebox就是日期框(只包含年月日的日期),在数据传到后端时,发生类型转换错误
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors Field error in object 'roleChengwei' on field 'endTime': rejected value [2022-08-28]; codes [typeMismatch.
Deep High-Resolution Representation Learning for Human Pose Estimation (CVPR 2019 oral)
文章地址:https://arxiv.org/abs/1902.09212
源码:GitHub - leoxiaobin/deep-high-resolution-net.pytorch: The project is an official implementation of our CVPR2019 paper "Deep High-Resolution Representation Learning for Human Pose Estimation"
1、 引言 HRNet(High-Resolution Net)针对2D人体姿态估计(Human Pose Estimation或Keypoint Detection)任务提出的,且该网络主要是针对单一个体的姿态评估。即网络输入只能是单个人。人体姿态估计主要的应用场景为:人体行为动作识别、人机交互、动画制作(比如根据人体的关键点信息生成对应卡通人物的动作)等等。
对于Human Pose Estimation任务,现在基于深度学习的方法主要有两种:
1)基于regressing的方式,即直接预测每个关键点的位置坐标。
2)基于heatmap的方式,即针对每个关键点预测一张热力图(预测出现在每个位置上的分数)。
注:当前检测效果最好的一些方法基本都是基于heatmap的,所以HRNet也是采用基于heatmap的方式。
2、HRNet网络结构 现在存在的大多数方法,都是提取图象的低分辨率特征,然后恢复成高分辨率特征进行预测。我们提出的方法,在整个网络中,主要以高分辨率为主。论文的核心思想就是不断地去融合不同尺度上的信息,也就是论文中所说的Exchange Blocks。
HRNet的架构。它由并行的高分辨率到低分辨率的子网组成,在多分辨率子网之间进行重复的信息交换(多尺度融合)。水平方向和垂直方向分别对应网络的深度和特征图的尺度。
3、相关工作 传统的单人位姿估计方法大多采用概率图形模型或图形结构模型,最近通过深度学习,自动提取特征方式,相对于传统的算法,提升是比较明显的。现在深度学习提出的解决方式主要分为两类,分别为关键点位置回归,以及估算关键点热图。
大多数网络都包含了一个主干网络,类似于分类网络一样,其降低了分辨率。以及另外一个主干网络,其产生与其输入具有相同分辨率的特征图。然后利用该特征去回归关键点或者估算热图。其主要是采用了分辨率 high-to-low 以及 low-to-high 的结构。可能增加多尺度融合和中间(深层)监督。
4、实现方法 人类姿态估算,关键点检测,输入为一张行人图像,输出为与关键点个数相同的特征图数量,由每个特征图预测对应的关键点。首先使用2个strided的卷积,减少输入图像的分辨率,获得初步特征图,然后把该特征图作为一个主体网络的输入,该主体网络的输出和输入的分辨率一样,其会其估算关键点的heatmaps。
4.1、连续多分辨率子网络 有的位姿估计网络是通过串联高分辨率子网来建立的,每个子网形成一个stage,由一系列卷积组成,并且在相邻的子网之间有一个下样本层来将分辨率减半。
4.2、并行多分辨率子网 首先我们在第一个 stage 开始了一个高分辨率的网络分支,然后逐步增加高分辨率到低分辨率的子网路,形成一个新的 stages,并将多分辨率子网并行连接。因此,后一阶段并行子网的分辨率由前一阶段的分辨率和一个更低的分辨率组成,一个包含4个并行子网络的网络结构示例如下:
4.3、重复的多尺度融合 我们引入了平行网络信息交换单元,比如每个子网络重复接受来自其他平行子网络的信息。下面是一个例子,展示了信息交换的方案。我们将第三 stage 分为几个(例如3个)交换模块,每个模块由3个并行卷积单元和一个跨并行单元的交换单元组成,其结构如下:
创建一个跑马灯TextView,继承AppCompatTextView
public class MarqueeTextView extends AppCompatTextView { private boolean isMarqueeEnable = false; public MarqueeTextView(Context context) { super(context); } public MarqueeTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public MarqueeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void setMarqueeEnable(boolean enable) { if (isMarqueeEnable != enable) { isMarqueeEnable = enable; if (enable) { setEllipsize(TextUtils.TruncateAt.MARQUEE); } else { setEllipsize(TextUtils.TruncateAt.END); } onWindowFocusChanged(enable); } } public boolean isMarqueeEnable() { return isMarqueeEnable; } @Override public boolean isFocused() { return isMarqueeEnable; } @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.
如有错误,恳请指出。
Yolov7的原作者就是Yolov4的原作者。看论文的时候看到比较乱,这里可能会比较杂乱的记录一下我觉得有点启发的东西。对于yolov7的代码,我也没有仔细的看,只是大概的看了下其他博客提到的些细节。所以这里也不会具体的解析代码。
文章目录 1. 相关工作2. 网络结构2.1 ELAN2.2 SPPCSPC2.3 MP 3. 样本分配策略4. 辅助损失5. 实验结果与总结 1. 相关工作 我觉得yolov7论文的 Related work 的前两小节写得指导性很大。
当前目标检测的主要优化方向:更快更强的网络架构;更有效的特征集成方法;更准确的检测方法;更精确的损失函数;更有效的标签分配方法;更有效的训练策略
同时还介绍了下模型的重参数化,可以将其看成是一种集成技术。现在可以将模型的重参数化分成两类:模块级集成(module-level ensemble)和模型级集成(model-level ensemble)。
对于模型级重参数化有两种常见的做法,一种是用不同的训练数据训练多个相同的模型,然后对多个训练模型的权重进行平均。另一种是对不同迭代次数下的模型权重进行加权平均。对于模块级重参数化是在训练期间将模块拆分为多个相同或不同的模块分支,并在推理期间将多个分支模块集成为完全等效的模块。然而,并非所有提出的重新参数化模块都能完美地应用于不同的体系结构。 之后的内容,无论是看单独看文章还是单独看源码,其实都比较难直观的了解整个网路的结构,所以还是要借助其他大佬画图做笔记。
2. 网络结构 无论是在源码中还是在文章里,都无法像yolov6那样直观地查看整个yolov7的backbone,neck和head结构。所以这里也只能自行的配合源码来作图。不过,幸运的是,已经有不少大佬画出了结构图。详细解析见参考资料3,4。
yolov7网络的结构图 先来查看yolov7.yaml的配置,代码作了部分的删减
# parameters nc: 80 # number of classes depth_multiple: 1.0 # model depth multiple width_multiple: 1.0 # layer channel multiple # anchors anchors: - [12,16, 19,36, 40,28] # P3/8 - [36,75, 76,55, 72,146] # P4/16 - [142,110, 192,243, 459,401] # P5/32 # yolov7 backbone backbone: # [from, number, module, args] [[-1, 1, Conv, [32, 3, 1]], # 0 .
问题:
本机Win10使用pycharm对远程服务器的代码进行debug时出现Can’t run remote python interpreter: Can’t get remote credentials for deployment server…
解决方法:setting-》project-》python interpreter-》show all,删掉多余的解释器,只留下用的那一个
很多人想做一款属于自己的编译器吧!今天,我们来做一款简易TK创建器。
分析:
1,询问模式
2,根据模式输入指令个数
3,创建GUI和该指令
4,显示
首先,我们要询问模式。这边分为2种模式,分别是单个组件和单个GUI,既然要询问,那就有输入input指令,看一看输入源代码吧!
print('您可选择单个组件或单个GUI') a=input('请选择模式:') 接着,我们就要对输入的模式进行判断,if语句不可少。
if a=='单个GUI': #下方代码等会讲解 elif a=='单个组件': #下方代码等会讲解 进入下一个步骤。首先,确定好GUI组件(button/label/entry/text)
然后将它打印出来
print('您可选择单个组件或单个GUI') a=input('请选择模式:') print('您可使用button/label/entry/text') 输入组件个数
a=int(input('指令个数:')) 但是输入内容不是整数就会报错
Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> a=int(input()) ValueError: invalid literal for int() with base 10: 'a' 所以需要异常处理
try: a=int(input('指令个数:')) except: print('请输入一个整数!') #请写在if语句中 这样就不会报错啦!
既然输入了指令个数,那就得输入多次,用for语句获取GUI指令
for i in range(a): a=input('GUI指令') 接着需要判断输入内容,用if语句
if a =='label': #代码后面讲 但只有一个组件太孤单了!elif语句
if a =='label': #后面讲 elif a=='button': #后面讲 elif a=='entry': #后面讲 elif a=='text': #后面讲 既然需要创建GUI,那就得导入tkinter的库。
安装dotnet-ef工具 dotnet-ef是对.Net命令行工具dotnet 的扩展, 例如创建并应用从旧模型到新模型的迁移,以及从现有数据库模型生成代码 命令如下 检查是否安装dotnet-ef为全局工具
dotnet tool list --global卸载现有版本
dotnet tool uninstall --global dotnet ef安装最新版本
dotnet tool install --global dotnet-ef安装指定版本
dotnet tool install --global dotnet-ef --version 5.0.0安装SqlServer的 NuGet包
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
同样添加 指定版本的包 需要末尾添加 --version 5.0.0安装设计包(用于迁移)
dotnet add package Microsoft.EntityFrameworkCore.Design CodeFirst模式定义EF Core 模型 EFCore使用约定、注解特性和Fluent API语句的结合,在运行时构建实体模型。 实体类表示表的结构,类的实例表示表中的一行 EF Core 约定 我们编写的代码都需要遵循以下约定
表的名称与 DBContext类(例如Products类)中的DbSet属性名匹配数据库列的名称与类中的属性名匹配,例如ProductID假定.Net类型为string 是数据库的 nvarchar类型对于名为ID的属性,如果类名为Product,就可以将此属性重命名为ProductID,那么这个属性是主键 EF Core 注解特性 约定通常不足以将类完全映射到数据库 可以向模型添加更多只能特性的简单方法就是应用注解特性 例如数据库中的名称中的最大长度为 40个字符,并不能为空
ProductName NVARCHAR(40) NOT NULL Description "NTEXT" 在类中,可以应用特性来指定名称的长度和不能为空
无论是服务端还是客户端,发送的数据格式为:
int(存储body的length作为head)+body
最简单的一种. 代码也非常简单.
网上没有看到这样的,经测试粘包都解决了.用得很好.
while(m_sock->bytesAvailable()) { int total = 0; // 需要获取的总大小 m_sock->read((char *)&total, sizeof(int)); qDebug() << "获取服务端返回的数据大小未:" << total; QByteArray data; int readNum = total; //把数据全部读完 while(true) { QByteArray tmp = m_sock->read(readNum); // QByteArray tmp = m_sock->readAll(); readNum -= tmp.length(); data.append(tmp); if(readNum > 0) m_sock->waitForReadyRead(); else qDebug() << "recv data from server:" << data; int r = dispatch(data); // 数据处理 // 说明数据是没办法转成json的,可能的情况是数据包接收的不对 if(r == -1){ qDebug() << data << "
【小程序错误】chooseAddress/chooseLocation/getLocation:fail the api need to be declared in …e requiredPrivateInfos field in app.json 自 2022 年 7 月 14 日后发布的小程序,若使用该接口,需要在 app.json 中进行声明,否则将无法正常使用该接口,2022年7月14日前发布的小程序不受影响。具体规则见公告
解决方法: 一、uniapp项目 在 manifest.json的mp-weixin节点中
加上:"requiredPrivateInfos": ["getLocation", "chooseLocation", "chooseAddress"]
二、原生小程序项目 在 app.json中加上:"requiredPrivateInfos": ["getLocation", "chooseLocation", "chooseAddress"] 即可
定义图结构体
typedef struct { VexType vexs[MAXNUM]; ArcType arcs[MAXNUM][MAXNUM]; int vexnum, arcnum; }AMGraph; typedef struct ArcNode { int adjvex; struct ArcNode *nextarc; /*AdjType weight; /*InfoType *info; }ArcNode; typedef struct VNode { VexType data; ArcNode *firstarc; }VNode, AdjList[MAXNUM]; typedef struct { AdjList vertices; int vexnum, arcnum; }ALGraph; 问题:
1、编写函数,构造以邻接矩阵表示的图;
2、编写函数,构造以邻接表表示的图;
3、编写函数,实现图的深度优先遍历算法(DFS);(图用邻接表表示)
4、编写函数,实现图的广度优先遍历算法(BFS);(图用邻接表表示)(选做)
5、编写函数,实现求最小生成树的Prim算法;(图用邻接矩阵表示)
6、编写函数,实现求最短路径的Dijkstra算法。(图用邻接矩阵表示)(选做)
算法思想阐述:
邻接矩阵创建无向网:
输入总顶点数和总边数后,初始化该矩阵,使其每个权值初始化为极大值,调用LocateALGVex函数,确定两个顶点在图中的位置后,赋予相应边的权值,同时使其对称边赋予相同的权值。
邻接表创建无向图:
输入总顶点数和总边数后,使每个表头结点的指针域初始化为NULL,输入每条边依附的两个顶点后,将此边插入Vi和Vj对应的两个边链表的头部。
Prim算法:
特点:适用于稠密图,归并顶点,与边数无关
1. 从某顶点 u0 出发,选择与它关联的具有最小权值的边(u0, v),将其顶点加入到生成树的顶点集合U中
2. 每一步从一个顶点在U中,而另一个顶点不在U中的各条边中选择权值最小的边(u, v),把它的顶点加入到U中
3. 直到所有顶点都加入到生成树顶点集合U中为止
根据二叉树的特性,栈式创建结构体
typedef struct BiTNode{ char data; struct BiTNode *lchild,*rchild;//左右孩子指针 }BiTNode,*BiTree; typedef struct stack_1//顺序栈 { tree* a[MAX];//用来存树的每一个结点 int tag[MAX];//起标记作用,用于非递归建树和非递归后序遍历 int top;//始终指向当前栈中元素的下一个位置,下标从零开始 }stack; 问题:
1、编写函数,创建一棵二叉树(递归算法和非递归算法);
2、编写函数,按树状结构输出二叉树;
3、编写函数,用递归算法分别求二叉树的各种遍历序列;
算法思想阐述:
递归建树:
依次读入输入的字符序列ch,若ch是一个’#’字符,则表明该树为空树,否则:
申请一个结点空间T;将ch赋给T->data;递归创建T的左子树;递归创建T的右子树; 非递归建树:
分别定义树根、左子树、右子树的结构体指针,用先序遍历的顺序分别对树根、左子树、右子树进行入栈操作,当左右子树都已建立时,进行出栈操作;
树状输出二叉树:
最直观的打印二叉树,只能用队列记录二叉树的层次遍历,并记录每个节点的层数及这层里的列数,最后再调整位置打印输出。这样的方法实现起来非常麻烦,所以本报告采用逆90度输出的方法打印二叉树。
每个节点都是独立的一行,记录当前是第几层次,根据层数控制输出位置。从最右子树开始输出,再输出根结点,最后输出左子树。
先序递归遍历:
采用递归,先判断节点是否为空,如果为空则返回。先打印当前节点内容,然后依次递归所有左子节点,再依次递归所有右子节点。
中序递归遍历:
采用递归,先判断节点是否为空,如果为空则返回。先依次递归所有左子节点,然后打印当前节点内容,再递归所有右子节点。
后序递归遍历:
采用递归,先判断节点是否为空,如果为空则返回。先依次递归所有左子节点,再递归所有右子节点,最后打印当前节点内容。
一、各函数代码如下
解题一: //入栈 void push(stack* st, tree* t){ st->a[st->top] = t; st->top++; } //出栈,即删除栈顶元素,并返回指向已删除元素的指针 tree* pop(stack* st){ if (st->top != 0) { st->top--; return st->a[st->top]; } else return NULL; } //取栈顶元素,但不删除,返回指向栈顶元素的指针 tree* top(stack* st){ if (st->top !
分别定义两个结构体——串的定长顺序存储、串的堆式顺序存储
typedef struct { char ch[MAXSTRLEN+1]; int length; }SString; typedef struct { char *ch; int length; }HString; 问题:
1、编写函数,串用定长顺序存储表示来实现串的基本操作;
2、 编写串的匹配算法,实现查找功能。
算法思想阐述:
BF算法:首先S[1]和T[1]比较,若相等,则再比较S[2]和T[2],一直到T[M]为止;若S[1]和T[1]不等,则S向右移动一个字符的位置,再依次进行比较。如果存在k,1≤k≤N,且S[k+1…k+M]=T[1…M],则匹配成功;否则失败。
该算法最坏情况下要进行M*(N-M+1)次比较, 时间复杂度为O(M*N)。
一、各函数代码如下 解题一:
//生成串 Status StrAssign(SString &S,char chars[]) { int i; S[0]=strlen(chars); for(i=0;chars[i]!='\0'&&i+1<MAXSTRLEN;i++){ S[i+1]=chars[i]; } S[i+1]='\0'; return OK; } //输出串 void PrintStr(SString S) { int i; for(i=1;i<S[0]+1;i++){ cout<<S[i]; } cout<<endl; } //求串长 Status StrLength(SString S) { return S[0]; } //判空 Status StrEmpty(SString S) { if(S[0]==0){ return OK; } else{ return FALSE; } } //比较字符串 Status StrCompare(SString S,SString T) { int i=0; for(i=0;i<S[0]&&i<T[0];i++){ if(S[i]!
JDK,全称Java Development Kit,(又称为Java SDK,Java Software Development Kit) ,是 Java 语言的软件开发工具包。
JDK中包含JRE(Java Runtime Environment)和Java开发工具包,JRE又包含jvm虚拟机和Java运行所需的核心类库。
JDK下载完之后,展示在咱们眼前的其实就是一个文件夹,JDK是分版本的,目前国内使用的版本大部分还是JDK8,也有小部分公司在使用JDK14。拿JDK8来举例,我们下载JDK并解压到一个空的文件夹中,然后点开这个文件夹,里面会有一大堆文件夹和文件。
根目录下有个bin文件夹,这里面放的都是Java的可执行文件(也可以理解为Java开发工具,可执行文件就是利用这个文件可以对写出来的代码进行一些操作,比如javac是用来编译的,这个javac就是其中的一个可执行文件),
还是这一级目录,有个lib文件夹,这个文件夹的作用就是对bin目录下的开发工具起支持作用的,说白了就相当于一个环境,在这个环境的支持下,bin目录下的开发工具才能起作用。
还有个jre文件夹,这个就是上面所所的JRE(Java Runtime Environment Java运行环境),此目录下有个bin文件夹,这里面放的是java运行的必要组件(就是传说中的jvm),同一级目录下有个还有lib文件夹(注意,这里的lib文件夹,和根目录下的lib文件夹作用是不同的),这里的lib是对jvm进行支持的,也可以认为是一个环境,有了这个环境,jvm才能运行起来。
总的来说,只要是用JDK开发的程序,就必须在JRE上运行。
Error in curl::curl_download(url, destfile, mode = mode, quiet = quiet, : Timeout was reached: [] Operation timed out after 119999 milliseconds with 903544832 out of 1237679690 bytes received Solution:
没办法,试了很多,只能自己手动下载
DocuController中使用keDD.properties里面的值,代码如下
这个只是内存中改写值,并没有实际在文件中改写值:
Properties sysConfig = new Properties(); InputStream inputStream = DocuController.class.getResourceAsStream("/keDD.properties"); try { sysConfig.load(inputStream); } catch (IOException e) { e.printStackTrace(); } String result=sysConfig.getProperty("EXCEL_DIR");// try { System.out.println(new String(result.getBytes("ISO-8859-1"), "gbk")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } sysConfig.setProperty("EXCEL_DIR","c:"); System.out.println(sysConfig.get("EXCEL_DIR")); 内存中更改了值,实际也更改了值如下(2022年8月26日08:51:39)
Properties sysConfig = new Properties(); InputStream inputStream = Controller.class.getResourceAsStream("BaseAddr.properties"); try { sysConfig.load(inputStream); sysConfig.setProperty("LOOK_FOR_ROAD_ECX_DWORDPTR","xxxx"); sysConfig.setProperty("LOOK_FOR_ROAD_ECX_DWORDPTR1","xxxx1"); sysConfig.setProperty("LOOK_FOR_ROAD_ECX_DWORDPTR2","xxxx3"); //这里加true意思是往文本里追加内容,否则就是覆盖前面的内容 //flush的意思是把上面set的内容一次性追加写入文本 FileOutputStream fos = new FileOutputStream( "src/sample/BaseAddr.properties",true); sysConfig .store(fos, "这里是注释"); fos.flush(); fos.close(); } catch (IOException e) { e.
signed int在计算机内部表示 简述为什么是二进制?int在计算机内部作为数值型数据存储补码 简述 程序代码在经过编译汇编等过程,转换成一条条机器指令,就像单词由一个个字母组成,计算机看待数据都是由 整数、浮点型和位串等几类简单的数据类型组成。而这些基本类型在计算机内部又都是由二进制构造而成。
为什么是二进制? 数字化的世界都是由现实的世界转换而来,这个转换过程需要媒介,电压的高低能很好的表示0和1两种状态,满足离散化和编码两种能力,由于计算机设备本身的硬件限制,只能离散化数据,对数据取样。编码则是需要通过制定一定的规则来模拟还原现实信息。
采用二进制有如下好处:
1、状态稳定,只有0、1两种状态。
2、二进制编码简单,对于机器实现来说计算简单。
3、01和逻辑命题真、假对应,与逻辑门契合。
int在计算机内部作为数值型数据存储 指令系统能够识别基本数据类型,基本数据类型被分成数值型数据和非数值型数据,int属于数值型数据。
补码 因为补码的运算很简单,实现了加减法的统一,无论是正数还是负数,该数的补码为:
[ X T ] 补码 = M + X T ( m o d M ) , 其中 M 为模数 [X_T ]_{补码} = M + X_T(mod M) ,其中M为模数 [XT]补码=M+XT(modM),其中M为模数
两数加减均可以用两数的补码和求模来表示。
因为补码位数为n时,整数 2 n − 1 2^{n-1} 2n−1补码为:
[ 2 n − 1 ] 补码 = [ 2 n + 2 n − 1 ] ( m o d 2 n ) = 10 ⋅ ⋅ ⋅ 0 ( n − 1 个 0 ) [2^{n-1}]_{补码} = [2^n + 2^{n-1}] (mod 2^n) = 1 0···0(n-1个0) [2n−1]补码=[2n+2n−1](mod2n)=10⋅⋅⋅0(n−1个0)
This beta version of Typora is expired,please download and install a newer version.
目录
1.Win+R键打开并输入cmd
2.在命令行窗口中 输入regedit 回车打开
3.打开注册表编辑器,找到路径为“计算机\HKEY_CURRENT_USER\SOFTWARE\Typora”
4.右键设置权限,将Admin的权限都设置为拒绝。
最后即可正常打开Typora
解决方法:
1.Win+R键打开并输入cmd 2.在命令行窗口中 输入regedit 回车打开 3.打开注册表编辑器,找到路径为“计算机\HKEY_CURRENT_USER\SOFTWARE\Typora” 4.右键设置权限,将Admin的权限都设置为拒绝。 最后即可正常打开Typora。
一、前言 三目运算符,又称条件运算符,是计算机语言(c,c++,java等)的重要组成部分。它是唯一有3个操作数的运算符,有时又称为三元运算符。
定义:
对于条件表达式b ? x : y,先计算条件b,然后进行判断。如果b的值为true,计算x的值,运算结果为x的值;否则,计算y的值,运算结果为y的值。
一个条件表达式绝不会既计算x,又计算y。
条件运算符是右结合的,也就是说,从右向左分组计算。例如,a ? b : c ? d : e将按a ? b : (c ? d : e)执行。
可以理解为条件 ? 结果1 : 结果2 里面的?号是格式要求。也可以理解为条件是否成立,条件成立为结果1,否则为结果2。
注意:在C语言中,结果1 和 结果2的类型必须一致。
a ? b : c简单理解方式为:
if(a) { return b; } else { return c; } 一个C语言中的例子解释:
int a = 2; int c = 3; int b = (a > c) ? a : c; cout << "
本文捋一捋imx6ull的uboot的启动流程。
首先,NXP提供的uboot经过编译最终烧写进存储介质中的是uboot.imx文件,这个imx后缀的文件不同于传统的比如S3C2440最终烧写的uboot.bin文件。
imx文件是在bin文件的基础上加上了一个头部,IMX6ULL的BOOTROM程序会根据拨码开关的高低电平选择对应的启动介质,从中读取这个头部信息,然后对头部信息进行解析,头部中最重要的是一个叫DCD表的东西。
DCD表中包含了时钟寄存器的地址和寄存器的值,引脚复用寄存器地址和寄存器的值,DDR控制器的寄存器地址和寄存器的值。imx6ull内部的BOOTROM程序会根据DCD表的内容打开时钟,初始化外部DDR。因此NXP提供的uboot代码的汇编阶段没有初始化时钟和初始化DDR的相关汇编代码!这也是NXP的uboot和传统的三星提供的uboot的重大区别。
除了上述标黑部分的区别以外,还有一个区别就是,IMX6ULL的BOOTROM程序会根据解析出来的链接起始地址在一开始就把整个Uboot源码读取到DDR中去,也就是说Uboot的第一行代码就运行在DDR中,这是不同于三星的传统Uboot的,传统Uboot的第一句代码是运行在片内SRAM上的。
由于上述的区别,Uboot的重定位过程也就不同了,IMX6ULL的重定位过程是把Uboot整体从DDR的起始地址给挪到DDR的后端地址上去,给Linux内核腾位置。而三星Uboot中重定位是从Flash中把Uboot加载到DDR中去,这也算是一个不同之处。
除了上述提到了几点不同以外,Uboot代码的其他部分功能基本大差不差:
IMX6ULL在uboot的汇编阶段要做的事情仅仅包含了:
一、设置CPU运行模式为SVC模式,关闭FIQ和IRQ中断。
设置成SVC模式是为了让CPU可以使用SoC的各种资源。
关中断因为启动过程不允许被打断,否则可能发生错误。
二、设置CP15协处理器的若干个寄存器,包括了设置异常向量表重定位并写入异常向量表的地址,失效Cache,关闭MMU,清除Cache。
重定位异常向量表是因为,我们知道异常向量表的地址是0x00000000,而Uboot是在DDR中运行的,DDR的起始地址肯定不是0x00000000,那么异常向量表必须重定位,这里只是配置好了重定位所需要的寄存器内容,实际的向量表重定位由后续的relocate_vector函数完成。
关闭MMU是因为,MMU负责虚拟地址到实际物理地址的转换,此时还没有加载操作系统,操作的都是实际物理地址,不需要MMU来转换。
清除Cache是因为,Cache的内容是从DDR中缓存过来的,起始阶段DDR中还没有我们加载的内容,此时从DDR中缓存内容到Cache中,如果CPU从Cache中取数据可能导致错误。
三、设置芯片内部的IRAM,划分出一部分用来作为堆栈(方便后续调用C函数),划分一部分用来存储uboot中的重要变量:struct global_data,这个结构体中包含了cpu的时钟频率信息,总线的时钟频率,uboot重定位的地址,外部DDR的大小,Uboot本身的大小的起始地址与终止地址,malloc内存池的大小和位置等等众多重要数据,这些结构体内部的变量是在后续的board_init_f()函数中被初始化的,这些变量被初始化完成以后,Uboot会根据其值进行重定位和一系列对外设的操作。
上述就是imx6ull的uboot在汇编阶段做的事情,下面在arch/arm/lib/crt0.S文件中的_main函数中会调用若干个C函数。
一、board_init_f_alloc_reserve,用于设置内部IRAM,划分出malloc区和存储global_data变量的区域,并将这个变量的地址写入R9寄存器中。
二、board_init_f_init_reserve,将上述函数划分的存储空间进行清零,把早期malloc区的地址写入到global_data结构体变量的malloc_base成员中去。
三、board_init_f,用于初始化部分外设和初始化global_data,这个函数里面有一个函数数组,函数数组中的函数会被依次执行,以此来实现初始化部分外设和global_data,这个函数数组在common/board_f.c文件中定义。这里初始化的外设主要是串口,定时器,初始化global_data主要是初始化其中的地址成员,比如uboot重定位以后的地址,malloc区的基地址,新的global_data变量的地址(因此刚开始global_data是放在内部的IRAM中的)。这个函数执行以后,外部DDR由原本的一张白纸变成了一段一段划分好的区域,每一段用于存储不同的内容。
四、 relocate_code,就是重定位代码,这个重定位是从DDR到DDR的重定位,因为对于imx6ull来说,一开始uboot就被加载到了DDR上去运行,重定位就是为了把DDR前面的位置空出来以加载Linux内核,该函数定义在文件 arch/arm/lib/relocate.S中。
五、 relocate_vectors,重定位向量表。
六、board_init_r函数,该函数和board_init_f一样,其中有一个函数数组,其中的函数会依次执行,这个函数的作用也是初始化外设,初始化那些在board_init_f函数中没有初始化过的外设, 比如该函数会初始化中断,网络信息,控制台以及存储设备等等。注意:函数数组中有一个叫board_init的函数,这个函数就是imx6ull的板级初始化函数,该函数定义在board/freescale/mx6ullevk/mx6ullevk.c文件中。我们进行Uboot移植的时候如果需要增减代码,基本就是在这个文件中进行代码的编辑。
至此,uboot启动的主要部分就结束了。
接下来进入交互界面等待命令,主要包含三个函数。
一、run_main_loop,如果3秒倒计时按下任意键,进入uboot的命令模式,否则启动Linux内核。
二、cli_loop,解析命令行命令函数。
三、cmd_process,执行相应的命令。
uboot启动Linux内核的过程:
一般进入到uboot命令以后,我们会使用tftp命令或者nfs命令把zImage加载到DDR中去,然后使用bootz命令启动,使用bootz命令就调用了第一个函数。
do_bootz:这个函数主要干三件事。
第一,调用bootz_start函数,这个函数会调用do_bootm_states执行BOOTM_STATE_START阶段;
设置images的ep变量,即系统镜像的地址。
调用bootz_setup去验证镜像;最后调用bootm_find_images查找设备树文件,放在images->ft_addr成员变量中。
第二,关中断
第三,设置images结构体变量的os成员,这个成员也是个结构体变量,设置它为IN_OS_LINUX。然后执行do_bootm_states函数,该函数使用参数标识不同的启动阶段,此时的启动阶段为BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |BOOTM_STATE_OS_GO。
do_bootm_states中会根据images.os.os这个系统类型来查找对应的系统启动函数,这里找到的是do_bootm_linux;do_bootm_linux函数最终会调用boot_jump_linux函数,这是uboot跳转到linux执行的最后一个函数。
boot_jump_linux函数调用了一个叫做kernel_entry的函数,这个函数是Linux内核定义的,Linux内核镜像文件的第一行代码就是kernel_entry,该函数有三个参数,第一个参数是0,第二个参数是机器ID,第三个参数是ATAGS或者设备树首地址。images->ep中保存着Linux内核镜像的起始地址,即Linux内核的第一行代码。
一旦开始执行kernel_entry,uboot的生命周期就结束了。
参考资料——《正点原子Linux驱动开发手册》
目录 ffmpeg的下载、配置下载版本说明环境变量配置 ffmpeg处理m3u8 | ts的常用命令 ffmpeg是一个十分强大的音视频处理工具,提供转码、播放等基础功能,功能十分全面、强大,但命令繁多复杂,通常不直接使用,而是集成在带GUI的视频处理软件中。
我用过一些m3u8的下载、合并工具,比如
TS Master:界面复杂,不好用TS助手:下载速度快,但有些情况下载合并不了,比如带有key的 推荐使用TS助手,TS助手下载、合并失败的再使用ffmpeg进行处理。
ffmpeg的下载、配置 下载 官方github只提供了源码,需要自行编译,BtbN提供了编译好的版本,可以在BtbN的github地址下载,也可以从ffmpeg官网链接进入BtbN
github下载地址:https://github.com/BtbN/FFmpeg-Builds/releases官网下载地址:http://www.ffmpeg.org/download.html 选择操作系统对应的任意一个版本下载即可
版本说明 1、GPL
GNU General Public License的缩写,GNU公共许可证,通常具有“传染性”,如果项目中集成了GPL协议的开源项目时,项目本身也会被感染成为GPL的开源项目,即需要将其开源、免费。
比如,我开发一个桌面exe程序时,需要引用某个GPL的开源的dll,那我开发的这个exe桌面程序也会变成GPL开源项目,我在项目发布时需要将其开源、免费,有义务且必须提供整个项目的源代码。
2、LGPL
GNU Lesser General Public License的缩写,GNU 宽松公共许可证,允许项目通过类库引用(link)的方式使用LGPL类库,项目可以作为商业软件进行发布、销售,发布时不需要开源、免费,但前提是不能修改引用的LGPL许可的软件部分。
开源免费的项目选GPL,不开源的项目选LGPL,相比于GPL而言,LGPL更灵活,开不开源均可。
3、-shared
ffmpeg的gpl、lgpl版本默认将共享的链接库集成到了exe中,bin中只有三个exe文件,这三个exe文件体积都很大;
-shared没有将共享的链接库集成到exe中,bin中有一堆链接库,这些链接库可以被3个exe共用,3个exe的体积都很小,压缩包体积也比gpl、lgpl的小得多。
环境变量配置 bin下的三个exe
ffmpeg 用于视频转码ffplay 用于视频播放ffprobe 用于获取音视频文件的信息 在PATH环境变量中添加bin目录,方便后续使用这些命令,主要是使用ffmpeg.exe。
ffmpeg处理m3u8 | ts的常用命令 #合并ts文件,ts文件数量少、不需要key的时候可以用这种, ffmpeg -i "1.ts|2.ts|3.ts|" -c copy xxx.mp4 #也可以根据文件中指定的ts文件位置进行合并,不需要key ffmpeg -i index.m3u8 -c copy xxx.mp4 #需要key的要加上参数-allowed_extensions ALL ffmpeg -allowed_extensions ALL -i index.m3u8 -c copy xxx.mp4 # -protocol_whitelist指定白名单协议,添加相应的协议即支持对应的文件来源 ffmpeg -allowed_extensions ALL -protocol_whitelist "
添加sloppypar实现文中自动换行、对齐:
\begin{document} \begin{sloppypar} 内容 \end{sloppypar} \end{document} 参考:
latex一行文字超出正文边界(设置自动换行,两端对齐):https://blog.csdn.net/nccccc12345/article/details/119702051?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2&utm_relevant_index=2
最近在一个项目中要实现一个时间段合并的业务,即多个可能重复的时间段,合并成一个时间段(应该是取并集)
这里要感谢某技术交流群里的叫嗯*的小哥,给我提供了解决方案
public class EnhanceDataUtil extends DateUtil { /** * 日期合并 -Date * 多个时间段 是有可能部分重合的 汇聚成一条时间线 * 比如8:00-9:00、8:30-10:30、10:40-11:10、10:36-11:00 * 最后输出:8:00-10:30、10:36-11:10 * * @param sourceDateRanges 输入多个时间范围的时间段列表 * @return 合并压缩成只有开始和结束时间的时间段列表 */ public static List<Date[]> amalgamateDate(List<Date[]> sourceDateRanges) { // 防止入参被篡改 深拷贝 sourceDateRanges = JSONUtil.toBean(JSONUtil.toJsonStr(CollUtil.emptyIfNull(sourceDateRanges)), new TypeReference<List<Date[]>>() { }, true); // 返回值 List<Date[]> amalgamateList = new ArrayList<>(); // 数据处理 sourceDateRanges = sourceDateRanges.stream() // 去掉空元素 .map(ArrayUtil::removeNull) // 只保留元素个数大于1的 .filter(sourceDateRange -> ArrayUtil.length(sourceDateRange) > 1) // 只保留start和end .
运输层,应用层 5. 运输层5.1. 概述5.2运输层端口号、复用与分用的概念5.3 UDP和TCP的对比5.4 TCP的流量控制5.5 TCP的拥塞控制5.6 TCP超时重传时间的选择5.7 TCP可靠传输的实现5.8 TCP的连接建立与释放5.8.1 TCP的运输连接管理——TCP的连接建立5.8.2 TCP的运输连接管理——TCP的连接释放 5.9 TCP报文段的首部格式 6. 应用层6.1 应用层概述6.2 客户/服务器方式(C/S方式)和对等方式(P2P方式)6.3动态主机配置协议DHCP6.4 域名系统DNS(Domain Name System)6.5 文件传送协议6.6 电子邮件6.9 万维网 计算机网络微课堂(有字幕无背景音乐版)
5. 运输层 之前课程所介绍的计算机网络体系结构中的物理层、数据链路层以及网络层它们共同解决了将主机通过异构网络互联起来所面临的问题,实现了主机到主机的通信。
5.1. 概述 运输层协议由称为端到端协议
5.2运输层端口号、复用与分用的概念 5.3 UDP和TCP的对比 用户数据报协议UDP
传输控制协议TCP
5.4 TCP的流量控制 5.5 TCP的拥塞控制 慢开始算法:拥塞窗口值按指数规律增长
拥塞避免: 拥塞窗口值+1
5.6 TCP超时重传时间的选择 5.7 TCP可靠传输的实现 5.8 TCP的连接建立与释放 5.8.1 TCP的运输连接管理——TCP的连接建立 TCP链接建立要解决以下问题:
①使TCP双方能够确知对方的存在;
②使TCP双方能够协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等);
③使TCP双方能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配。
建立连接时,为什么不用两报文握手来建立TCP连接?
注意:
(1)TCP的标准规定,SYN=1的报文段不能携带数据,但要消耗掉一个序号
(2)TCP的标准规定,普通的确认报文段如果不携带数据,则不消耗序号。
5.8.2 TCP的运输连接管理——TCP的连接释放 5.9 TCP报文段的首部格式 6. 应用层 6.1 应用层概述 6.2 客户/服务器方式(C/S方式)和对等方式(P2P方式) 6.
转载文章 版权声明:本文为ZhongGuoRenMei原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/ZhongGuoRenMei/article/details/109056282
LTE网络注册流程(1) 最近在学习lte网络相关内容,并做些简单记录,并且结合高通平台的qxdm log做简要分析。
首先,介绍下lte注册的总体流程,后续再完善整体结构,至于lte注册信令流程,网上搜索就有一堆的资料,笔者就不再上传了。
1.设备开机,plmn 选择,小区选择
plmn = mcc+mnc
过程如下,开机后,先从设备sim卡中获取上次注册的plmn,如果获取不到,则需要进行全频段扫描,获取一个Plmn列表,从中选择一个Plmn,选定Plmn后根据其对应频点找到同步信号,与小区进行时频的同步,从而获取小区的系统信息。
2.接收系统信息
系统信息,下图主要表示各个消息的作用。
接收到系统信息后,测量小区信号强度,判断小区是否能够驻留。小区的系统信息允许终端驻留,终端就会驻留到目标小区,并且选中该小区所在的Plmn,至此,目标小区就变成终端的服务小区。
3.随机接入
随机接入是终端与基站建立联系的过程,随机接入后,终端才能实现与小区的同步。与随机接入相关的消息MSG1-MSG4.
随机接入后,终端与基站建立起srb1,成功建立了rrc连接,后面就需要进行附着过程。
4.附着,建立默认承载
lte附着过程存在两个特点
双向鉴权,建立默认承载
附着过程包括,请求附着,获取终端用户id,鉴权,启动Nas信令安全通信,接受附着,建立srb2和默认承载完成附着。如下图
至此,lte注册流程就跑完了,下面贴上一张终端log图
关于附着之前的log没有抓取到,这里就不贴出来来了,后面我会对没一步具体介绍也做个简单的记录的,特么一些消息的重要信息
LTE网络注册流程(2) 本文说说lte系统信息吧,分为Mib和sib,sib系统信息存在sib1-sib12,主要介绍下mib,sib1 sib2 sib3 sib4 sib4的作用。
mib:
mib包含了非常重要的系统信息,包括小区标识(PCI),频率,当前系统帧号,发射天线个数及小区带宽,phich信道配置。
sib1信息
sib1也是重要的系统信息,终端附着到lte网络必不可少的,包含如plmn值,小区选择准则,小区工作频段指示,其他sib消息的调度信息。
sib2信息
sib2也携带了重要的系统信息,比如小区是否阻塞,是否允许UE驻留小区,所有物理信道配置,一些重要定时器的配置。
各种信道配置我也没整明白,下面就说主要定时器的作用。
T300:RRC 连接建立定时器
T301定时器:RRC重建定时器
T310:下行失步定时器
N310: 接受失步指示最大数目,与t310相关
等
如果需要了解,请自己去查下lte 各种定时器作用。
SIB3消息
包含了小区选择和重选的相关信息。
sib4,sib5
sib4包含lte网络内同频相邻小区信息,sib5包含lte网络内异频或者不同频段的相邻小区信息,主要包含了该小区的具体频率值,重选该小区的门限,小区重选优先级等。
关于小区重选准则如下:
1.向优先级高的小区重选
高优先级小区信号强度在指定时间内大于指定门限
ue在当前服务小区驻留时间大于1s
2.向同优先级小区重选
同等优先级小区的信号强度必须指定时间内持续大于当前服务小区信号强度
ue在当前服务小区驻留时间大于1s
3.向低优先级小区重选
没有优先级等于或者高于当前服务小区的优先级的小区的信号强度大于指定门限
当前服务小区信号强度小于指定门限
候选的低优先级小区信号强度指定时间内持续大于指定门限
ue在当前服务小区驻留时间大于1s
关于小区测量准则
1.如果服务小区是最高优先级小区,其信号强度持续时间内高于指定门限 不测量
微信小程序 -快速上手 第一个小程序 成功 导入一个已经在开发中项目 vscode 设置 设置高亮 拷贝到 settings.json
"files.associations": { "*.wxml": "html", "*.wxss": "css", }, 重启 vscode 打开 wxml 文件 观察 有没有高亮
安装小程序开发插件 小程序目录结构 配置文件 全局配置 app.json pages 字段 只能在微信开发者工具中 编辑 pages字段,按下保存 才生效!!!
pages 快速创建页面的时候 在里面创建即可
"pages": ["pages/index/index","pages/index3/index3"], 作用:
快速创建页面的 只能在微信开发者工具编辑代码才行指定页面启动顺序 window 窗口
"window": { "navigationBarBackgroundColor": "#ffda11", "navigationBarTitleText": "拼多多123", "navigationBarTextStyle": "yellow" }, tabBar "tabBar": { "selectedColor": "#e64a19", "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "icons/home.png", "selectedIconPath": "
文章目录 1、请你说说线程和协程的区别2、请你说说MySQL索引以及它们的好处坏处3、请你说说多线程4、说说怎样保证线程安全5、请你说说死锁定义及发生的条件6、亲你说说进程间通信方式7、说说你对MVC的理解8、详细说说Redis的数据类型9、请你说说乐观锁和悲观锁10、设计模式了解吗11、说说你对AOP的理解12、说说Redis的持久化策略13、请你讲讲单例模式,手写一下单例模式14、说说虚拟内存与物理内存的区别15、说说你对IOC的理解16、请你说说内存管理17、请你说说IO多路复用(select,poll,epoll)18、请你说说线程和协程的区别19、请你说说MySQL的事务隔离级别20、如何利用Redis实现一个分布式锁21、请说说你对反射的了解22、请你说说ArrayList和LinkedList的区别23、数据库为什么不用红黑树而用B+树24、请你说说Redis的数据类型25、请你说说ConcurrentHashMap25、说说缓存穿透,击穿,雪崩的区别26、Redis如何与数据库保持双写一致性27、说说你了解的线程同步方式28、请你说说innodb和myisam的区别?29、String、StringBuffer、StringBuilder有什么区别?30、说说了解的JVM内存模型31、说说JVM的垃圾回收机制32、说说类加载机制33、请你说说抽象类和接口的区别34、请你说说==和equals()的区别35、说说synchronize的用法及原理36、说说你对AQS的理解37、Java那些地方使用了CAS38、说说JVM的垃圾回收算法39、请你说说Redis数据类型中的zset,它和set有什么区别?底层是怎么实现的?40、说说static修饰符的用法41、说说线程的状态42、说说你对ThreadLocal的理解43、说说Spring Boot常用的注解44、说说Bean的生命周期 1、请你说说线程和协程的区别 得分点:
地址空间、开销、并发性、内存。
标准回答 :
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
进程有独立的地址空间,线程有自己的堆栈和局部变量,单线程之间没有单独的地址空间;进程和线程切换时,需要切换进程河鲜城的上下文,进程的上下文切换时间开销远远大于线程上下文切换时间,耗费资源较大,效率就要差一些;进程的并发性较低,线程的并发行较高;每个独立的进程有一个程序运行的入口,顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制;系统在运行的时候会为每个进程分配不同的内存空间,而对线程而言,除了CPU外,系统不会为线程分配内存,(线程所使用的资源来自其所属进程的资源)线程组之间只能共享资源;一个进程奔溃后,在保护模式下不会对其他进程产生影响,但是一个线程奔溃整个进程都会死掉,所以多进程要比多线程健壮。 2、请你说说MySQL索引以及它们的好处坏处 得分点:
检索效率,存储资源,索引维护。
标准回答 :
索引就像表行的指针,是一种允许查询操作快速确定哪些行符合WHERE子句中的条件,并检索到这些行的其他列值的数据结构。
索引主要有普通索引、唯一索引、主键索引、外键索引、全文索引、复合索引几种;在大数据量的查询中,合理使用索引的优点: 大幅提高匹配where条件的检索效率;用于排序和分组操作的加速; 索引使用不当的坏处: 索引必定会增加存储资源的消耗;同时增加了插入、更新、删除操作的维护成本,因为每个增删改查操作后相应列的索引都必须被更新 加分回答: 只要创建了索引,就一定会走索引吗,不一定。比如在使用组合索引的时候,如果没有遵从“最左前缀”的原则进行搜索,则索引是不起作用的;假设在id、name、age字段上已经成功建立了一个名为Multildx的组合索引,索引行中按id、name、age的顺序存放可以搜索id、(id、name)、(id、name、age)字段的组合,如果列不构成索引最左面的前缀,那么MySQL不能使用局部索引,如(age)或(name,age)组合咋不能使用该索引查询。 3、请你说说多线程 得分点:
线程与进程的关系,为什么使用多线程。
标准回答 :
线程是操作系统调度的最小单元,它可以让一个进程并发的处理多个进程,所以,在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈、局部变量,并且能够共享进程内的资源,由于共享资源,处理器便可以在这些线程之间快速切换,从而让使用者感觉这些线程在同时执行。
总的来说,操作系统可以同时执行多个任务,每个任务就是一个进程,进程可以同时执行多个任务,每个任务就是一个线程,一个程序运行之后至少有一个进程,而一个进程可以包含多个线程,但至少要包含一个线程,使用多线程会给开发人员带来显著的好处;使用多线程的原因主要有以下几点: 更多的CPU核心。现代计算机处理器性能的提升方式,已经从追求更高的主频向追求更多的核心发展,所以处理器的核心数量会越来越多,充分的利用处理器的核心则会显著的提高程序的性能,而程序使用多线程技术,就可以将计算逻辑分配到多个处理器核心上,显著减少程序的处理时间,从而随着更多处理器核心的加入而变得更有效率;更快的响应时间。我们进程要针对复杂的业务编写出复杂的代码,如果使用多线程技术就可以将数据一致性不强的操作派发给其他线程处理,(也可以是消息队列),如上传图片,发送邮件,生成订单等。这样的响应用户请求的线程就能够尽快的完成处理,大大的缩短了响应时间,提升了用户体验;更好的编程模型。Java为多线程提供了良好且一致的编程模型,使开发人员能够更加专注于问题的解决,开发者只需要为此问题建立合适的业务模型,而无需绞尽脑汁的考虑如何实现多线程。一旦开发人员建立好了业务模型,稍加修改就可以将其方便的映射到Java提供的多线程编程模型上。 4、说说怎样保证线程安全 得分点:
原子类、volatile、锁。
标准回答 :
Java保证线程安全的方式有很多,其中较为常用的就三种,按照资源占用情况由轻到重排列,这三种保证了线程安全的方式分别是原子类,volatile、锁。
jdk从1.5开始提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一种用法简单,性能高效,线程安全的更新基本类型、原子更新引用类型,原子更新属性,原子更新数组。无论原子更新哪种类型,都要遵循“比较和替换”规则,即比较要更新的值是否等于期望值,如果是则更新,如果不是则失败;volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”,从而可以保证单个变量读写时的线程安全,可见性问题是由处理器核心的缓存导致的,每个核心均有各自的缓存,而这些缓存均要与内存进行同步,volatile具有如下的内存语义:当一个volatile变量时,该线程本地内存中的共享变量的值会被立刻刷新到主内存;当读一个volatile只能保证单个共享变量的线程安全,锁则可以保证临界区内的多个共享变量的线程安全。java中加锁的方式有两种,分别是synchronized关键字和Lock接口,synchronized是比较早期的API,在设计之初没有考虑到超时机制,非阻塞形式,以及多个条件变量,若想通过升级的方式让它支持这些相对复杂的功能,则需要大改它的语法结构,不利于兼容旧代码,因此,jdk的开发团队就在1.5之后新增了Lock接口,并通过Lock支持了上述的功能,支持响应中断,支持超时机制,支持以非阻塞的方式获取锁,支持多个条件变量(阻塞队列)。加分回答: 实现线程安全的方式又很多种,除了上述三种方式之外,还有如下几种方式: 无状态设计。线程安全问题是由多线程并发修改共享变量引起的,如果在并发环境中,没有设计共享变量,则自然就不会出现线程安全问题。这种代码实现可以称作“无状态实现”,所谓状态就是指共享变量。 不可变设计。如果在并发环境中,不得不设计共享变量,则应该优先考虑共享变量是否为只读的,如果是只读场景就可以将共享变量设计为不可变的,这样自然也不会出现线程安全问题。具体来说就是在变量前加final修饰符,使其不可被修改,如果变量是引用类型,则将其设计为不可变类型。 并发工具java.util.comcurrent包提供了几个有用的并发工具类,一样可以保证线程安全,:Semaphore:就是信号量,可以控制同时访问特定资源的线程数量。CountDownLatch:允许一个或多个线程等待其他线程完成操作。CyclicBarrier:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会打开,所有被屏障拦截的线程才会继续运行。 本地存储:我们也可以考虑使用ThreadLocal存储变量,ThreadLocal可以方便地为每一个线程单独存一份数据,也就是将需要并发访问的资源复制成多份,这样一来,就可以避免多线程访问共享变量了,它们访问的是自己独占的资源,它从根本上隔离了多个线程之间的数据共享。 5、请你说说死锁定义及发生的条件 得分点:
争夺共享资源,相互等待,互斥条件,请求和宝成条件,不剥夺条件,环路等待条件。
标准回答 :
死锁。两个或两个以上的进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或系统产生了死锁。这些永远在相互等待的进程称为死锁进程。产生死锁的必要条件: 互斥条件:只进程对所分配到的进程进行排他性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其他进程请求资源,则请求者只能等待,直到占有资源的进程用完释放;请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源请求已被其他进程占有,此时请求进程阻塞,但又对自己已获得的其他资源保持不放。不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完成时自己释放;环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,…,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,…,Pn正在等待已被P0占用的资源。 6、亲你说说进程间通信方式 得分点:
管道,命名管道,信号,消息队列,共享内存,内存映射,信号量,Socket。
标准回答 :
管道:管道也叫无名(匿名)管道,它是UNIX系统中IPC(进程间通信)的最古老形式,所有的UNIX系统都支持这种通信机制,管道的本质其实是内核中维护的一块内存缓冲区,Linux系统中通过pipe()函数创建管道,会生成两个文件描述符,分别对应管道的读端和写端。无名管道只能用于具有亲缘关系的进程间的通信。命名管道:匿名管道,由于 没有名字,只能用于亲缘关系的进程间通信,为了克服这个缺点,提出了有名管道(FIFO),也叫命名管道、FIFO文件。有名管道(FIFO),不同于匿名管道之处在于它提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与FIFO创建进程不存在亲缘关系的进程,只要可以访问该路径就能够彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据;信号:信号是LInux进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称为软件中断,它是在软件层面上对中断机制的一种模拟,是一种异步通信的方式,信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理一个突发事件;消息队列:消息队列就是一个消息的链表,可以把消息看作是一个记录,具有特定的格式以及特定的优先级,对消息队列有写权限的进程就可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程则可以从消息队列中读消息,消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程则可以从消息队列中读走消息,消息队列是随内存持续的;共享内存:共享内存允许两个或者多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会称为一个进程用户空间的一部分,因此这种IPC机制无需内核介入,所有需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用,与管道等要求发送进程将数据从用户空间的缓冲区复制进内核内存和接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种IPC技术的速度更快;内存映射:内存映射(Memory-mapped I/O)是将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件;信号量:信号量主要用来解决进程和线程间并发执行时的同步问题,进程同步是并发进程为了完成共同任务采用某个条件来协调它们的活动,对信号量的操作分为P操作和V操作,P操作是将信号量的值减1,V操作是将信号量的值加1,当信号量的值小于等于0之后,在进行P操作时,当前进程或线程会被阻塞,直到另一个进程或线程执行了V操作将信号量的值增加到大于0之时;Socket套接字:就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象,一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制,Socket一般用于网络中不同主机上的进程之间的通信。 7、说说你对MVC的理解 得分点:
MVC概念,model,view,Controller模块功能。
标准回答 :
Model代表的是数据,view代表的是用户界面,Controller代表的是数据的处理逻辑,它是Model和View这两层的桥梁。将软件分层的好处是,可以将对象之间的耦合度降低,便于代码的维护。Model:指从现实世界中抽象出来的对象模型,是应用逻辑的反应,它封装了数据和对数据的操作,是实际进行数据处理的地方(模型层与数据库才有交互)。在MVC的三个部件中,模型拥有最多的处理任务,被模型返回的数据是中立的,模型与数据格式无关,这样一个迷信能为多个视图提供数据,由于应用于模型的代码只需要写一次就可以被多个视图重用,所以减少了代码的重复性。View:负责进行模型的展示,一般就是我们见到的用户界面。Controller:控制器负责将视图和模型之间的交互,控制对用户输入的响应,响应方式和流程;它主要负责两方面的动作,一是把用户的请求分发到相应的模型,二是把模型的改变及时地反映到视图上。 加分回答
为了解耦以提升代码的可维护性,服务端开发一般会对代码进行分层,服务端代码一般会分为三层:表现层,业务层,数据访问层。在浏览器访问服务器时,请求会先到达表现层,最典型的MVC就是jsp+servlet+javabean模式,以JavaBean作为模型,既可以作为数据模型来封装业务数据,又可以作为业务逻辑模型来包含应用的业务操作,JSP作为视图层,负责提供页面来为用户展示数据,提供相应的表单(form)来用于用户的请求,并在适当的时候(点击按钮)向控制器发出请求来请求模型进行更新。Serlvet作为控制器,用来接收用户的请求,然后获取请求中的数据,将之转换为业务模型需要的数据模型,然后调用业务模型相应的业务方法进行更新,同时根据业务执行结果来选择要返回的视图,Spring MVC框架是基于Java的实现了MVC框架模式的请求驱动类型的轻量级框架。前端控制器是DispatchServlet接口实现类,映射器处理器是HandlerMapping接口实现类,视图解析器是ViewResolver接口实现类,页面控制器是Controller接口的实现类。 8、详细说说Redis的数据类型 得分点:
Redis的5种数据结构。
布局类 布局类主要是确定一些控件的排布方式
垂直布局QVBoxLayout 直接拖拽垂直布局到编辑界面,然后讲按键拖拽进去发现自带按照垂直方式进行排列。
1.1 设置边距和间隔 代码 通过setContentsMargins(20, 20, -1, -1)设置左上右下的边距,-1表示默认值
通过setSpacing设置控件之间的间隔
designer 在designer表示边距为layoutLeftMargin、
layoutTopMargin、layoutRightMargin、layoutBottomMargin
间距为layoutSpacing
通过setStretch设置各个部分的占比 分别为:1/6 2/6 3/6
self.verticalLayout.setStretch(0, 1) self.verticalLayout.setStretch(1, 2) self.verticalLayout.setStretch(2, 3) 2、水平布局
2、边距和间隔 通过setContentsMargins(-1, -1, 20, -1)设置左上右下的边距,-1表示默认值通过setSpacing设置控件之间的间隔 3、比例分配 通过setStretch设置各个部分的占比 分别为:1/6 2/6 3/6
self.horizontalLayout.setStretch(0, 1) self.horizontalLayout.setStretch(1, 2) self.horizontalLayout.setStretch(2, 3) 公众号
前言:相信看到这篇文章的小伙伴都或多或少有一些编程基础,懂得一些linux的基本命令了吧,本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。
文章使用到的的工具
Python:一种编程语言,只能进行后端数据的处理和管理前端HTML文件,不能用来处理HTTP请求Nginx:web服务器,用于处理HTTP请求uWsgi:Python的第三方库,建立Nginx和Python环境的交互通信Django:Python的Web开发框架,部署HTML文件和数据库 接下来我将通过下列 云服务器一步一步演示,
0.创建云服务器(选择免费的即可) 1.连接云服务器 1.0.0通过idea的ssh进行连接 输入你的云服务器ip与密码,用户名默认是ssh,连接成功后会有一个控制台出来
在这个控制台中就可以操作您的云服务器了 1.0.1建立文件连接 配置好之添加文件映射(你本机的项目地址与服务器项目地址),后点击浏览远程主机
效果如下,可以直接看到目标虚拟机中的文件
1.0.2把项目所需文件上传进云服务器 右击项目文件->部署->上传到sfy
2.服务器端应用的安装 2.0 组件流程图 nginx:用作转发反向代理,基本所有服务器都会使用到这个,可能有人要问不使用可以不?如果只是测试的话可以不使用,python,java中的web容器能够承载的并发量过小,使用nginx可以提高承载量,举个例子:在不使用nginx时同时有100个人访问你的网站,你的网站挂了,如果使用nginx的话1000个人同时访问也没什么问题。
uwsgi:监听端口,主要监听nginx转发过来的请求进行处理。
组件了解即可,会安装使用就好了,不必要去深究
下载地址
关注公众号,回复0193-1994
NAT是什么? 在五层的网络协议中,网络层位于数据链路层和传输层之间,网络层的主要工作,简单概括就是——针对网络中的任意两台主机,规划一条通信路径,完成数据的传输。
网络层中有一个重要的协议:IP协议,该协议可分为 IPV4和IPV6。
NAT 即 网络地址转换,用来转换 私有IP地址和公有IP地址,这个转换服务在路由器中进行。
NAT,就是为IPV4协议的应用产生的,NAT的转换服务是为了节约数量有限的IPV4公有IP地址。
起初发明IPV4地址时,工程师们对IPV4的地址容量设置为40亿+个(2^32)
私有IP地址?
私有IP地址:属于非注册地址,不能通过私有IP地址直接访问互联网,私有IP地址只能在内部只有,例如在家中、企业内部…
路由器 会给网络内部的设备分配一个私有IP地址
公有IP地址?
公有IP地址:属于注册地址,可以直接访问互联网
当家庭或企业内部的网络设备想要访问互联网时,路由器在内部就会将它们的私有IP地址进行转换,转换成互联网服务提供商分配的公有IP地址。
当互联网上的某台计算机A想要和私有网络中的设备进行通信,那么此时的路由器就会将计算机A发来的公有IP地址转换为设备的私有IP地址
NAT的三种实现方式
静态转换:一个私网IP地址对应一个公网IP地址,是一对一的关系动态转换:私网中有100个主机,那么公网中就要有100个IP地址,但是私网中的100个主机和公网的100个IP地址不是一对一的对应关系,而是动态分配IP地址端口多路复用:私网中有100个主机,有100个私网IP地址,但是对外访问只需要一个公网IP地址,使用这一个公网IP地址的不同端口 在将来,如果互联网都使用IPV6协议,那么就不再需要NAT服务,不再需要IP地址转换了,那时候每一台网络设备都有属于自己的公有IP地址。
1. 什么是同构 同构的项目支持客户端渲染和服务器端渲染客户端渲染缺点 首屏速度加载慢不支持 SEO 和搜索引擎优化首页需要通过请求初始化数据 2.Next.js Next.js 英文文档,Next.js 中文文档 是一个轻量级的 React 服务端渲染应用框架默认支持服务端渲染自动根据页面进行代码分割基于页面的客户端路由方案基于 Webpack 的开发环境,支持热模块替换可以跟Koa或者其它Node.js服务器进行集成支持 Babel 和 Webpack 的配置项定制静态文件服务 public 3.项目初始化 3.1 创建项目 mkdir zhufengnextjs cd zhufengnextjs npm init -y npm install react react-dom next redux react-redux --save npm install axios express body-parser cors express-session connect-mongo mongoose koa koa-router --save 3.2 添加脚本 package.json
{ "scripts": { "dev": "next dev", "build": "next build", "start": "next start" } } .gitignore
3.3 访问服务 以 .
win11返回win10
目录
回退原因 方法一:使用Windows更新选项退回Win10
方法二:重装系统
回退原因 最近,Microsoft不是推出了win11吗,然后我就试了试,结果卡的一批,bug还很多(最重要的是玩MC还会闪退,网上的方法都试过了,没用,后来感觉是win11的问题,结果我看很多人也有这样的问题)。虽说它的生态环境比以前好了很多,但是我还是忍不下去,下定决心退回win10,下面是我给大家整理的一些问题:
右键菜单整合为一二级菜单多任务界面不再支持时间线显示文件资源管理器占用资源巨大开始菜单仅能显示18个图标并且不支持文件夹文件夹不支持缩略图时间没办法精确到秒UI五世同堂: 我也是查了很久的资料,有用的,没用的,我试了一遍。这里给大家提个小建议,以后有问题,先在B站上搜,里面的阿婆主都十分的良心。
下面就是我总结的两个方法,希望能帮到大家哦 <(> - <)>
方法一:使用Windows更新选项退回Win10 前提:更新win11后10天内有效!!!
操作流程:点设置——>更新及安全——>Window更新——>恢复——>返回
如果它问你为何回退,这时你就可以亲切的问候它一下了,嘿嘿😏
当出现下面的页面是,要注意选:“不,谢谢”
方法二:重装系统 疑难解答:这个方法不会丢失除了C盘外的文件,就是说所有自定义下载到C盘的都会被格式化,但prom gramse files×86和其他盘(像D盘)都不会被删,然后系统环境变量会被清除,完成之后桌面会只剩回收站和edge,以前的用户文件和多媒体文件在:Windows.old文件夹里面;如果你在保存数据那里可以选择保存个人数据的话,就全部都会保留下来
①工具原料:下载 Windows 10 (microsoft.com)
啊,对了,有些人会在MSDN(不是CSDN啊,别搞错了) 上下载,但是上面的比较旧,不是很建议在哪里下,而且还要激活密钥,很容易毁掉
②开始下载:
注意,这里选立即升级,不要选创建介质,我这里勾错了,忘改了
③保存内容:
这里比较有争议啊,它问你要保存什么的时候,有些是可以点保存个人数据,有些则不行,我就不行,因为那里是灰的,点不了。具体情况看上面,我选不了,所以C盘系统文件没保存,大家注意一下。如果C盘真没什么重要的东东,我就不建议选了,以前很多人选了后,在后面下载时会卡在某一个点
④最后的话就没什么问题了,大家按照指示来选就🆗了
啊,干净的win10我的爱!记得点赞加关注,下次不迷路。
网上找了很多都不理想,要不就是params为null,取不到值,要不就是格式模板不理想,今天自己整理去做了一下,记录今天操作
1.选择File–>Settings–>Editor–>File and Code Templates进入到下图位置分别设置Class/interface/Enum文件
类模板
/** *@ClassName ${NAME} *@Description TODO *@Author wuning *@Date ${DATE} ${TIME} *@Version 1.0 */ 2.设置方法注解快捷键等模板
选择File–>Settings–>Editor–>Live Templates,先选择右侧绿色加号新建一个自己的模板组,如图命名为myGroup
3.选中已建好的组,选择右侧绿色加号新建模板,如下图。
4.方法注解模板
** * @description * @author wuning * @date $date$ $time$ $params$ * @return $returns$ */ 5.再填写获取参数等信息方法
date = date()
time = time()
returns = methodReturnType()
params 获取是通过脚本具体如下:
groovyScript("def result=''; def params=\"${_1}\".replaceAll('[\\\\[|\\\\]|\\\\s]', '').split(',').toList(); for(i = 0; i < params.size(); i++) {result+=' * @param\\t' + params[i] + '\\t' + ((i < params.
绿色框框选择数据,红色数据也跟着出来跟着绿色框框所对应的数据也一起出来
代码如下
$('#mcover').show();//显示
$('#mcover').hide();//隐藏
常变量与符号常量的区别 先来看一段代码:
要求:算出半径为10的圆的面积。
#include <stdio.h> #define PI 3.14 //定义符号常量 int main (void) { const double r=10; //定义常变量 double s; s=PI*r*r; printf("s=%lf",s); return 0; } 可以看出:二者出现的位置不同,常变量出现在函数体内,符号常量在函数体外。
获取值的方式不同。
还有两处不同:内存分配,define是宏定义,程序在预处理阶段将符号常量用define定义的内容进行了替换(在此之后,PI代表3.14)系统不为它分配内存。const定义的常变量,在程序运行时,系统需要为它分配内存空间。
编译时类型检查,define定义的符号常量,预处理时,直接进行替换。编译时不能进行数据类型检查。const定义的常变量,编译时进行严格的类型检验。
字符串常量
定义:用双引号(" ")括起来的0个或者多个字符组成的序列。
字符串常量是不可被修改的 。 存储:字符串常量以ASCII码的形式存储在内存中,每个字符串尾自动加一个'\0' 作为字符串结束的标志。'\0'在ASCII码表中也是一个字符,其 ASCII码值为0。
例子: "hello world" 存储在内存中时hello与world间的空格也要被存储起来,结尾加'\0'。空格与'\0'各占一个Byte。
字符串"a"与字符'a'不同,前者占2个Byte,后者占一个。
特别的,"123abc\n" '\n'在反义字符中,其只占一个Byte。 字符串函数 字符串函数也叫字符串处理函数,指的是编程语言中用来进行字符串处理的函数,如C,pascal以及LotusScript中进行字符串拷贝,计算长度,字符查找等的函数。
C中有大量操作字符串的函数,使用时,需要包含头文件<string.h>。
下面介绍两种字符串函数:strlen,strcmp。
strlen(s1)返回字符串s1的长度(不含'0')strcmp(s1,s2)若s1=s2,返回0;若s1>s2,返回一个大于0的数;若s1<s2,返回一个小于0的数 (strcmp形式)字符串的比较规则 :对两个字符串自左向右逐个字符相比(比较ASCII码值),直到出现不同的字符或'\0'为止。如果字符全部相同,则认为相等;若出现不相同的字符,以第一个不相同的字符比较结果为准。
规律:若两个字符串都由字母组成,则按字母顺序(a~z)在后面的为“大”,小写字母比大写字母“大”
例子:
#include <stdio.h> #include <string.h> int main (void) { printf("strlen=%d\n",strlen("hello world")); printf("strcmp=%d\n",strcmp("my","hello world")); printf("strcmp=%d\n",strcmp("my","my")); printf("strlen=%d\n",strlen("123abc\n")); return 0; }
1、如果你直接运行python程序 可以 PYTHONIOENCODING=utf-8 python3 test.py
2、如果你是用nohup后台运行,可以在jenkins中写 BUILD_ID=dontKillMe #保持后台运行python程序
export PYTHONIOENCODING=utf-8 #解决中文输出问题
nohup python3 -u client.py > test.log 2>&1 & sleep 10
题目描述 658. 找到 K 个最接近的元素
解法 双指针,既然数组已经排序好了,那么按照 x 就可以分两半边,两边各记录一个指针,分别比较着远离 x
class Solution { public: vector<int> findClosestElements(vector<int>& arr, int k, int x) { int right = lower_bound(arr.begin(), arr.end(), x) - arr.begin(); int left = right - 1; while (k--) { if (left < 0) right++; else if (right >= arr.size()) left--; else if (x - arr[left] <= arr[right] - x) left--; else right++; } return vector<int>(arr.begin() + left + 1, arr.
数据库删除表中数据有三种方法: 1、drop table 表名称
drop table hl_fly_demand 2、truncate table 表名称
truncate table hl_fly_demand 3、delete from 表名称 where 列名称 = 值
delete from hl_fly_demand where status='temp' drop,truncate,delete区别 1、drop (删除表):删除内容和定义,释放空间。简单来说就是把整个表去掉.以后要新增数据是不可能的,除非新增一个表。
drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid。
2、truncate (清空表中的数据):删除内容、释放空间但不删除定义(保留表的数据结构)。与drop不同的是,只是清空表数据而已。
注意:truncate 不能删除行数据,要删就要把表清空。
3、delete (删除表中的数据):delete 语句用于删除表中的行。delete语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存,以便进行进行回滚操作。
truncate与不带where的delete :只删除数据,而不删除表的结构(定义)
4、truncate table 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用delete。
如果要删除表定义及其数据,请使用 drop table 语句。
5、对于由foreign key约束引用的表,不能使用truncate table ,而应使用不带where子句的delete语句。由于truncate table 记录在日志中,所以它不能激活触发器。
6、执行速度,一般来说: drop> truncate > delete。
7、delete语句是数据库操作语言(dml),这个操作会放到 rollback segement 中,事务提交之后才生效;如果有相应的 trigger,执行的时候将被触发。
truncate、drop 是数据库定义语言(ddl),操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发 trigger。
目录
语义分割
1. 常见数据集格式
2. 常见语义分割评价指标
转置卷积
1. 运算步骤(s为步长,p为padding,k为卷积核尺寸)
2. 优势以及存在的问题
膨胀卷积
1. Gridding Effect网格效应
2. 小目标分割效果差的问题
3. 膨胀卷积的一些特点
FCN
DeepLabV1
DeepLabV2
DeepLabV3
LR-ASPP
UNet
U2Net
语义分割 语义分割(semantic segmentation),对每个像素进行分类;区别于实例分割和全景分割。
1. 常见数据集格式 PASCAL VOC:调色板(PNG图片,P模式,palette),边缘为255,损失计算过程忽略255MS COCO:针对图像中的每一个目标都记录了多边形坐标(polygons)分割结果:mask蒙版,每个像素数值对应类别索引 2. 常见语义分割评价指标 Pixel Accuracy(Global Acc):总共预测正确的像素个数 / 目标的总像素个数mena Accuracy:每个目标的Acc,然后目标求均值mean IoU:每个目标的IoU再求平均(常用,如下图所示) eg:目标A的IoU计算图示 转置卷积 💡 Transposed Convolution,转置卷积,并不是卷积的逆运算,主要用于upsampling。图像分割和图像生成等任务需要图像恢复到原尺寸,这个将图像由小分辨率映射到大分辨率的尺寸恢复操作,叫做上采样。(待补充图像处理中常用的上采样操作,eg:最近邻插值、线性插值、双线性插值、双三次插值) 1. 运算步骤(s为步长,p为padding,k为卷积核尺寸) 在输入特征图元素间填充s-1行,0列
在输入特征图四周填充k-p-1行,0列 将卷积核参数上下、左右翻转
做正常卷积运算(padding0,stride1)
eg: s=1,p=0,k=3;step1:元素间填充0行;step2:四周填充2行; step3:卷积核参数上下左右反转;step4:正常卷积 转置卷积操作后特征图的大小可以通过如下公式计算:
其中stride[0]表示高度方向的stride,padding[0]表示高度方向的padding,kernel_size[0]表示高度方向的kernel_size,索引[1]都表示宽度方向上的。通过上面公式可以看出padding越大,输出的特征矩阵高、宽越小,你可以理解为正向卷积过程中进行了padding然后得到了特征图,现在使用转置卷积还原到原来高、宽后要把之前的padding减掉。
原文链接:https://blog.csdn.net/qq_37541097/article/details/120709865
2. 优势以及不足 与传统的上采样方法相比,转置卷积具有可学习的参数,上采样方式并非预设的插值方法。可通过网络来获取最优的上采样方式应用场景: 图像生成任务DCGAN中,生成器将随机输入变成一个全尺寸图片,这里用到了转置卷积语义分割中,解码器中使用转置卷积,eg:FCN、UNetCNN可视化,通过转置卷积将CNN的特征图还原到像素空间,以观察特定特征图对哪些模式的图像敏感但是,转置卷积会导致生成图像中出现网格/棋盘效应(checkerboard artifacts) 膨胀卷积 💡 Diated convolution又叫做空洞卷积,在保持卷积参数量不变的情况下:①增大卷积感受野;②保持原输入特征图长和宽不改变。
在Android中,访问data目录是需要root权限,但是有个例外那就是/data/local/tmp目录,这个目录通过使用ADB来打开就会拥有读写权限!
注意:
(1)这个目录不能一级一级的打开,需要通过cd /data/local/tmp 一次性进入
(2)该目录下的文件属性,权限都是可以修改的,例如chmod 777 /data/local/tmp/xxx
示例操作如下(以下为单条可执行命令):
adb shell cd /data/local/tmp/ adb shell mkdir -p /data/local/tmp/ adb push android_server /data/local/tmp/ adb shell chmod 755 /data/local/tmp/android_server adb shell /data/local/tmp/android_server 在没有Root的情况下,可以把自己的sh脚本放到这个目录赋予执行权限,就可以运行了
注意:需要使用ADB来访问这个目录才有读写权限
Kali系统学习:弱点扫描工具NMAP实战演示 目录 Kali系统学习:弱点扫描工具NMAP实战演示1 Nmap介绍2 Nmap常用命令3 实战演练3.1 搜寻网络内活跃的主机3.2 扫描目标主机3.3 查找漏洞使用方法(可跳过)3.4 端口扫描3.5 利用脚本检查漏洞 4 后话 1 Nmap介绍 Nmap,是 Network Mapper 的缩写,由 Gordon Lyon 维护(更多关于作者 Mr. Lyon 的信息可以访问这个网页: http://insecure.org/fyodor/) ,并被世界各地许多的安全专业人员使用。它被默认安装于我们的kali系统中,无需另外安装,使用起来非常方便,再因其功能的强大,深受网络安全有关从业人员的喜爱。
其实有关Nmap还有一个图形化界面的前端版本,名为Zenmap,Zenmap是Nmap的GUI版本,由Nmap官方提供,通常随着Nmap安装包一起发布。Zenmap是用Python语言编写的,能够在Windows、Linux、UNIX、Mac OS等不同系统上运行。开发Zenmap的目的主要是为Nmap提供更加简单的操作方式。
但是在我还是由衷的建议大家学习命令行版本的Nmap,首先做主要的原因还是我个人认为命令行版本的Nmap使用起来比Zenmap更灵活,其次作为一名网络相关行业的人员,命令行(还有掉发…)是一个无法绕开的经历,如果你是老手了,命令行自然不会陌生,也没有难度,那么为什么不选择更灵活的Nmap呢?而如果你是一个新手,那么使用Nmap能帮助你更快熟悉Kali系统的使用,帮助你迅速进阶!
Nmap的基本功能主要有4个;
1、主机发现(Host Discovery)
2、端口扫描(Port Scanning)
3、版本侦测(Version Detection)
4、操作系统侦测(Operating System Detection)
2 Nmap常用命令 Nmap的功能十分强大,内含的各种参数也比较多,大家可以使用 nmap -h 查看详细操作说明
nmap -h #查看Nmap操作说明
结合我们的日常使用,在此我只列举些常用的参数供大家使用和参考:
参数注释-p指定扫描的端口号-v显示扫描过程-F执行快速扫描-n禁止反向域名解析-R反向域名解析-6启动ipv6扫描-Pn跳过主机发现的过程进行端口扫描-A全面扫描,该命令会扫描ip/域名的操作系统信息,版本信息,路径跟踪等等的内容,不过扫描速度嘛…,dddd-sSTCP SYN扫描-sUUDP扫描-sTTCP扫描-sV扫描系统版本可程序版本号检测–script=vuln全面的漏洞扫描-PA/-PS/-PR扫描局域网下包含的主机 3 实战演练 3.1 搜寻网络内活跃的主机 在渗透前期,我们首先先要确定在对应目标网段目前有多少的主机还"活着",以方便我们寻找目标,在这里由于我们是以演示为目的,我们的目标主机为我的Metasploitable 2,这里我们可以使用 nmap <目标ip> <参数> 的方式对我们的目标进行扫描
nmap 192.168.254.0/24 #对192.168.254.0/24网段内所有ip地址进行扫描
可以看见,我们在这个网段里共扫描出了4个在活动的主机,其中包括了这个主机正在开放的服务、端口、IP地址等等信息,非常的详细。
但是在日常的渗透测试中我们会发现,许多的操作系统都会对端口扫描流量进行处理,这将会导致我们出现下面这种情况
什么主机都扫不出来
所以,我在此分享一下日常使用的一个小技巧,我们可以添加 -sn 参数,禁用 nmap 对主机端口扫描的默认行为,让 nmap 只尝试 ping 主机,这样就可以扫描出来了,用这样的方式进行扫描相比于什么参数都不加,首先就是可以速度上有优势,其次就是防止主机对端口扫描数据进行处理导致我们什么都扫不出来这种现状。
汽车产业与技术链分析
参考文献链接
https://mp.weixin.qq.com/s/8No0u5yYSfDZ46zFEIdpQg
https://mp.weixin.qq.com/s/q4cXewR2lzOg-Eo4bMntyA
https://mp.weixin.qq.com/s/tzyq3tpiSOWTL_k0XI0hPw
https://mp.weixin.qq.com/s/NgrrCWnZxFlNTgVk7PKkcQ谁在?
https://mp.weixin.qq.com/s/7DBRyslROM3KEOT-KNTUkA在看啊我
400V?800V?高电压平台技术解析
全面系统地介绍了纯电动汽车技术,包括纯电动汽车的基本知识,如纯电动汽车的定义、组成、工作原理、驱动形式、特点、典型实例,以及纯电动汽车的动力蓄电池系统、电驱动系统、整车控制器和纯电动汽车的性能与仿真等。该书以工程应用为背景,通过大量图片及具体实例进行讲解,帮助读者掌握纯电动汽车初步设计所需的理论知识。
电动车会取代燃油车吗?电动车什么时候能像燃油车一样便利?虽然在动力、噪音、使用成本等方面电动车已经甩开燃油车一个身位,随着续航超过1000km的车型陆续亮相续航焦虑也得到了很大程度的缓解,但充电慢、充电难依旧是电动车无法颠覆燃油车的“阿喀琉斯之踵”。为了解决这一问题,高电压平台技术和与之配套的超级充电桩是目前最被看好的解决方案之一。那么,电压平台升高的量变如何使电动车实现便利性媲美燃油车的质变,距离这一技术的广泛应用又还有多长的路要走呢?
受限于硅基IGBT功率元器件的耐压能力,之前电动车高压系统普遍采用的是400V电压平台。基于该电压平台的充电桩中,充电功率最大的是特斯拉第三代超级充电桩,达到了250kW,工作电流的峰值接近600A。如果想要进一步提高充电功率、缩短充电时间,就需要将电压平台从400V提升到800V、1000V甚至更高的水平,来实现高压系统的扩容。
800V电压平台搭配350kW超级充电桩所能实现的充电速度,不仅比目前常见的120kW直流快充桩要快上很多,更逐步接近传统燃油车在加油站加油的使用体验了,尤其对于没有家用充电桩安装条件、充电依赖公共充电设施的用户来说是一大利好。而根据业内人士分析,在超级充电桩落地后,充电桩的最大充电功率有望达到600kW以上,“充电五分钟、续航200公里”也将从一句玩笑变成现实。到那时,还会担心电动车的充电问题吗?
与此同时,在用电功率相同的前提下,电压等级的提高还将减小高压线束上传输的电流,这将缩减高压线束的截面积,达到降低线束重量、节省安装空间的效果。
结果很简单但过程很复杂
虽然从实现的功能上来说,高电压平台技术看起来并不复杂,只是升高了整车的电压。但对于技术的开发和应用,却是“牵一发而动全身”的大工程。
目前主流的动力电池包,已经能够支持2C充电倍率(充电倍率是充电快慢的一种量度,充电倍率=充电电流/电池额定容量),通过电解液添加剂、各向同性石墨、石墨烯等材料的使用,可以一定程度上提升电池材料的电导率,改善高电压下三元材料的稳定性。但这些方案并不能从根本上避免副反应的发生,如果想要实现4C甚至6C充电倍率的超快充,还需要在电池材料、高控制精度的BMS(电池管理系统)等方面实现突破。
在电驱动系统方面,电压的提高会对绝缘能力、耐压等级以及爬电距离提出更高的要求,将对电气部件的设计和成本带来影响,但在工业电机等领域还是有比较丰富的高压应用经验可以借鉴,主要的难点在电机控制器的核心元件——功率半导体器件。目前满足车规级标准的功率半导体器件中,最主流的硅基IGBT耐压等级在600-750V,能在800V平台上使用的高压IGBT产品并不多,还存在着损耗高、效率低的缺点。
只是由于目前在产能和成本方面仍无法与IGBT相媲美,碳化硅器件的普及还需要时间,业内对2025年碳化硅MOSFET的渗透率预期普遍在20%左右,未来几年内IGBT仍将是电驱动系统最主流的功率半导体器件。
在空调压缩机、PTC、DCDC、车载充电机等部件方面,面向高电压平台的开发也在进行中。根据业界人士的分析,相关的量产工作均有望于今年年内完成,一旦产业链趋于成熟,可以快速拉低整个制造成本。
星星充电、普天新能源、特来电等充电服务商,均具备了400kW以上充电桩的技术储备。但目前采用的电动汽车充电标准还是2015年颁布的,最大电压和电流分别为950V、250A,最大充电功率被限制在240kW。充电桩落地,也将进一步推动相关产品的应用。
总的来说,电动车高电压平台技术所需的配套方案已经基本具备,何时进行高电压平台的量产开发工作、以何种方式应用这一技术的问题,已经摆在了各个车企面前。
车企在高电压平台方面的布局
在高电压平台方面,第一个吃螃蟹的是2019年上市的保时捷Taycan(参数|询价)。出于对充电速度和持续性能的追求,Taycan率先量产了800V电压平台,但作为“先行者”,保时捷也承担了相应的开发风险和挑战,受限于各零部件开发进度的不同,最初的Taycan并没有拿出一个完全由800V用电器组成的电压平台,并在电池的快充速度上进行了一定的妥协和让步。
不惜在车上增加如此复杂的电压转换设备,保时捷Taycan最主要的目的就是要缩短用户在充电上付出的时间成本。而在其他高压部件以及电池快充能力取得进步之后,保时捷Taycan及其后续车型还有望在350kW充电功率的基础上,进一步发掘出800V电压平台的潜力。
保时捷Taycan有些高不可攀,别急,高电压平台技术也在覆盖更多平民车型。现代汽车就在其E-GMP平台上使用了800V电压平台,基于此平台开发的IONIQ(艾尼氪)5已经完成亮相。
奔驰的EVA平台、通用的第三代纯电动平台、捷豹路虎的电气化平台,也都纷纷选择了800V作为车辆的运行电压。此外,虽然MEB平台的车型才上市不久,但大众也迫不及待地提出了Trinity项目,预计将于2026年应用800V超充技术。
比亚迪是较早布局相关技术的厂商。借助高压IGBT方案,比亚迪将e平台旗下车型的电压提升至了600V以上,唐新能源更是达到了700V。
此前专注于增程式方案的理想汽车,也计划在高压纯电动平台上推出多款纯电动车型,通过对400kW充电桩的支持,实现10分钟提升300-500km续航的补能速度。可以说,厂商在高电压平台方向上的开发工作也并不落后。
前景很美好但距离很遥远
虽然高电压平台+超级充电桩技术的发展,为电动车描绘出了一个美好的未来,但在落地推广的层面,还是陷入了“先有鸡还是先有蛋”的争执中。
对于整车厂来说,在没有基础设施配套的前提下,推出一款高电压平台的产品仍将使用户面临充电困难的问题。对此,北汽蓝谷、岚图汽车的相关人士均表示,虽然一直在关注高电压平台和超级充电桩技术的发展,但尚未有推出相关车型的打算。
无论是“车等桩”还是“桩等车”,整车厂和充电服务商的顾虑都是可以理解的,还需要在充电基础设施建设和电动车开发方向上,加以引导和推动。
编辑点评:
虽然电压平台的升高,意味着电动车诸多零部件的重新开发设计,以及高压充电网络从无到有的布局建设,让距离产品的普及还有很长一段距离要走。但就像快充技术改变了大家使用智能手机的习惯,电动车高电压平台技术的落地也会对电动车产品的技术走向和使用体验产生巨大的影响。当基于电压平台升高的量变,使电动车的便利性达到了媲美燃油车的质变,那么取代燃油车的那一天还会远吗?
自动驾驶芯片
智能芯片领域的独角兽——地平线,再度传出融资方面的信息。据相关报道,地平线计划融资1亿至2亿美元。至于IPO上市,地平线目前正在等待比较合适的时机,即等到市场有合理甚至高估值的时候,再寻求在香港进行IPO。
方案的优秀代表——地平线
当前,地平线已经陆续推出了征程2、征程3和征程5三款车规级芯片。其中,尤其值得一提的是,作为第三代的车规级芯片,征程5单颗芯片AI算力最高可达128TOPS,是面向L4高等级自动驾驶的大算力芯片。在今年4月,比亚迪就已经官宣和地平线达成合作,在旗下部分车型上搭载地平线的征程5芯片;不久之后,上汽也进一步官宣,将和地平线合力打造基于征程5和征程6的智驾计算平台。
在芯片法案通过,并且通过出口管制政策不断收紧包括EDA在内的各种芯片上下游工具的情况下,自主解决芯片供应问题已经成为当务之急。而地平线的征程系列,正日益成为方案的优秀代表。放眼市场,包括长安、奇瑞、上汽智己、广汽埃安、东风岚图、江淮以及上汽大通、理想、长城等都已经成为地平线的方案,就是最好的一个例证。
芯片企业新老业务对比
和出售芯片不同,地平线目前还在积极拓展帮助车企自研芯片的业务。对于车企来说,如果可以部署专属的芯片,必然能够提升车企的差异化竞争力,加快研发创新的速度。而地平线的这种模式,就是BPU授权模式。
作为最早布局自动驾驶领域的技术企业,Mobileye是将芯片架构、芯片、操作系统以及自动驾驶相关软硬件全部整合在一起。等Mobileye将开发工作全部完成后,再交付给车企。这种模式,对于车企来说,自动驾驶相关的软硬件完全是一个黑盒。与此同时,Mobileye的开发周期也可能高达6,7年。在当前自动驾驶技术发展日新月异的当下,Mobileye这种打包出售自动驾驶解决方案的模式,逐渐出现难以更好满足车企研发需求的情况。
而和Mobileye形成鲜明对比的是英伟达。其把GPU架构开发成芯片,然后包上自己的操作系统 CUDA。对于车企或者自动驾驶技术公司来说,可以基于英伟达的方案来设计自动驾驶软硬件,也就是说,在这种模式下,自动驾驶软硬件的开发和整车开发流程整合在了一起。一方面,车企有了更大的自主权,另外一方面,相关的开发周期能够被大大缩短。对于车企和自动驾驶技术公司来说,这种模式显然比Mobileye的模式更加先进和高效,而这也是导致英伟达如今在汽车自动驾驶领域比Mobileye更受欢迎的主要理由。
至于地平线,之前一直在推动所谓的Together OS 模式。在这种模式下,整车企业的开发工作不仅深入到自动驾驶这样的应用软件,还能够触达底层操作系统。这么做的做大好处在于推动量产的时间可以进一步缩短。而主机厂在前期介入的越深,就能够更好地调集各种资源,实现效率最大化。当前,地平线正在推动BPU授权模式。这种模式的最大特点就是打通芯片和整车之间的开发,让整车开发可以和芯片以及自动驾驶软硬件系统的开发实现高度协同。在这种情况下,车企可以为自己打造专属的自动驾驶芯片,实现整体芯片效率的最大化。之前特斯拉就是拥有自研芯片的能力,因此在算力上甚至能够超过英伟达,为AutoPilot和FSD提供最强的算力支持。而现在,地平线想做的就是将这种能力赋予更多车企。当然,芯片的专利依然牢牢掌握在地平线自己手中。
芯片,依然是资本眼中的香饽饽
不得不说,芯片半导体依然是资本最为青睐的领域。
除地平线之外,仅在今年上半年,芯驰科技完成了B+轮融资;芯擎科技得到了近10亿元A轮融资;至于黑芝麻智能则推进到了C+轮融资。无论是政策,还是投入的资源,现在的自上而下都会优先确保芯片所需的各种资源。根据公开数据显示,在过去的两年里,芯片半导体领域的年均融资规模都超过了2000亿元,而2022年上半年也有超过800亿元的融资额。像地平线、黑芝麻智能这样的公司,已经被视为英伟达、Mobileye这样深耕自动驾驶芯片公司的技术方案。
如果再结合地平线目前正在进行的新一轮融资以及提供全新的服务情况来看,地平线正在积极为自己寻找到新的收入来源。作为芯片设计公司,如果以英伟达为案例来看的话,其汽车领域的自动驾驶芯片,相比于英伟达起家的显卡芯片来看,所占比例相对较小。而对于地平线来说,在没有英伟达这样更多的业务领域涉足的情况下,基于手头现有的技术进行挖潜,是去提升收益的必经之路。也只有更为靓丽的财报和具备更强抵御冲击的能力,才能让地平线之后的IPO获得更高的估值。
RISC-V策略全解析!
在今日举办的2022 RISC-V峰会上,平头哥半导体副总裁孟建熠公布了阿里平头哥的最新“造芯”力作——无剑600。这次,平头哥发的不是芯片,而是一个高性能RISC-V芯片设计平台。一方面帮助芯片公司降低开发芯片成本和风险,另一方面向第三方IP和基础软件公司开放,让更多资源接入到RISC-V生态。“无剑”是剑法的至高境界,也寄予了平头哥的雄心:通过“授人以渔”,提供软硬件全栈支持,显著缩短芯片从研发到量产的时间,推动更多面向不同行业的高性能RISC-V芯片出现。更重要的是,将长期活跃于终端市场的RISC-V芯片,送上了通往高性能的高速列车。据孟建熠分享,无剑600已经实现与开源云端操作系统龙蜥的适配,并成功运行了大型桌面级软件和基于JAVA的应用。
01.“平台+SoC原型”双剑出鞘,突围高性能
近年起势迅猛的RISC-V,与x86、Arm一起,被业界并称为「三大CPU主流架构」。因开源开放、灵活、低功耗等特性,RISC-V具备没有历史包袱、没有高昂授权费用、开发成本更低、无需担忧“断供”风险等优势,被视作发展自主可控本土芯片生态的关键路径之一。平头哥在做的事,就是通过软硬件研发投入和生态连接,推动RISC-V生态快速壮大。今天发布的无剑600,便意在于此。无剑600是一个高性能RISC-V异构芯片的软硬件全栈平台,拥有高性能、高内存带宽、异构计算、人工智能(AI)加速的特点,同时兼具高安全、多模态感知和软硬一体的能力。
▲无剑600平台设计图
借助该平台,开发者和企业能够快速开发性能更高、主频更高、内存更大、边缘AI计算能力更强的SoC芯片。无剑600的默认处理器是玄铁C910,即平头哥在2019年发布的主频2.5GHz的高性能RISC-V核。这将RISC-V的商用性能,从1GHz推向2GHz。有何意义?孟建熠打个比方,假如将RISC-V比作跳高运动,原来只能跳1米高度,现在能稳定跳2米以上,突破了RISC-V性能的极限。在AI加速方面,无剑600平台中,最新版的玄铁C910集成了矢量Vector处理器,支持FP16等新型数据类型,并可提供高达4TOPs的Int8 AI算力,方便AI加速类应用的开发。无剑600平台还重点实现了CPU+XPU异构架构设计,从运行功耗、内存带宽和软件栈方面都进行了优化,并支持适配GPU、NPU、VPU、DSP、ASIC等多种不同功能的第三方IP。也就是说,半导体IP公司可以用无剑600来打造RISC-V整体生态上的能力,下游厂商进入了系统级、体系化的框架,可更高效地开发芯片。平头哥基于无剑600平台成功“打样”——SoC原型曳影1520。
▲SoC原型曳影1520
这是一颗多模态AI处理器SoC,内置4核玄铁C910,最高吞吐率4266MT,AI峰值算力达到4TOPS,支持全链路安全防护,具有4K视频处理能力,同时包含丰富的异构计算和高速IO资源,软硬件都已完成应用的适配。开发者和企业也可以在定制个性化芯片的同时,先在曳影1520上开发自己的系统,缩短产品从研发到量产上市的时间。
02.给软硬协同打个样!用RISC-V芯片跑大型桌面级软件
RISC-V芯片的参数迈进了高性能的大门,下一步,就是考察实际跑应用的表现。在曳影1520上,平头哥与龙蜥社区、中科院软件所PLCT实验室,联合打造从芯片平台到龙蜥操作系统、再到上层应用软件的全链路能力,已首次实现龙蜥Linux操作系统的兼容。
▲基于龙蜥Linux OS的软硬件全栈平台
这进一步拓展了RISC-V生态想象力。龙蜥操作系统(Anolis OS)是一款基于Linux的主流开源云计算操作系统,定位于服务器端,支持多种主流芯片架构和计算场景。因其丰富、复杂的软件栈,龙蜥对芯片的要求非常高,此前在x86、Arm架构上都经过了全面验证。这一次,龙蜥首次对RISC-V提供全面支持。曳影1520帮助RISC-V架构搭载更重的操作系统和应用,完成了3000多个基础组件的适配,并首次运行FireFox浏览器、LibreOffice等大型桌面级软件,以及Hexo和Open Rocket等基于NodeJS和JAVA的应用,这为更大型应用程序的适配奠定了基础。
▲图说:无剑600平台已适配龙蜥OS,并首次运行LibreOffice Writer/Impress/Calc等桌面级软件
转载自:
https://blog.csdn.net/liaoqingjian/article/details/109817110
OpenDroneMap OpenDroneMap 是一个开源的航拍图像处理工具,可以把航拍图像进行点云、正射影像和高程模型等处理,也可以做3维重构,生成3维模型。简直是个神器,做出来的效果和pix4d等软件差不多(我目前只做了无人机影像的正摄影像的生成)。
根据官方文档所说,PyODM可以很轻易的创建无人机影像的正射图,DEM,3d模型以及点云。
正射图的效果如下:
下面开始使用OpenDroneMap的python库PyODM。
注:若图片内有exif信息,则odm可以直接生成tif的正射影像。给图片添加exif信息可以参看下面这篇博客。
https://blog.csdn.net/weixin_43162240/article/details/103579657
1.环境准备 安装pyodm包 用pip安装即可。
pip install -U pyodm 1 启动docker pyodm需要启动docker,所以在使用之前还需要先安装docker.
windows的安装就不说了,可以自行百度,下面介绍linux系统下docker的安装。
# 安装docker $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ sudo apt-get install -y docker-ce 启动docker,这里会自己下载需要的包,所以需要等待一会儿。 $ sudo docker run -ti -p 3000:3000 opendronemap/nodeodm
1234567 基于ERDAS的遥感影像正射图的制作 application/pdf 0星 超过10%的资源 165KB 下载 2.python教程 大致流程是这样,先连接Node,然后新建任务,然后等待任务执行完成,最后获取结果。
这里关键是新建任务时有个option选项,可以设置一些配置。看文档说明得知,配置会影响处理速度与结果的质量。
对正射影像的制作来说,需要配置的一些参数(参数可以在官方文档查看)如下:
orthophoto-resolution:米/像素,表示地面分辨率dsm:是否要生成dsm,如果不需要的话,选false会加快处理速度ignore-gsd:设置为True就会跳过一些加速处理,得到的图像质量会好一些min-num-features:找的特征点数量,数量越多质量越好,处理时间越长texturing-nadir-weight:官方建议,在都市设为29-32,草地或者平底设置为0-6mesh-octree-depth:官方建议设置为10-11,增加建筑屋顶的平滑性。 注意:在制作正射影像时,选择的图片最好是有至少两个轨道的数据,一个航线的数据拼接会失败,即task.wait_for_completion()会返回failed.
import os from pyodm import Node import time from tqdm import tqdm image_dir = ‘testdata/’
因为google在点击打开新的网站时默认是覆盖当前的网页,在当前网页打开,当我们想看以前的网页时还要返回,很麻烦。这里我教大家三种办法去解决这个问题,实现在新页面打开网址
总共三种办法: (1)ctrl+鼠标左键:在点击网址之前,按住ctrl键,再点击左键打开 (2)鼠标中键:点鼠标中键直接打开 (3)ctrl+shift+左键:好像说了个寂寞,直接ctrl就能实现的功能,还要加一个shift。哎,这个办法大家看看就好
整体架构如上图所示,通过nginx反向代理amis-editor、zeebe和keycloak,docker可以通过以下compose文件安装:
# While the Docker images themselves are supported for production usage, # this docker-compose.yaml is designed to be used by developers to run # an environment locally. It is not designed to be used in production. # We recommend to use Kubernetes in production with our Helm Charts: # https://docs.camunda.io/docs/self-managed/platform-deployment/kubernetes-helm/ # For local development, we recommend using KIND instead of `docker-compose`: # https://docs.camunda.io/docs/self-managed/platform-deployment/kubernetes-helm/#installing-the-camunda-helm-chart-locally-using-kind # This is a full configuration with Zeebe, Operate, Tasklist, Optimize, Identity, Keycloak, and Elasticsearch # See docker-compose-core.
前言:本文的OLED多级菜单UI为一个综合性的STM32小项目,使用多传感器与OLED显示屏实现智能终端的效果。项目中的多级菜单UI使用了较为常见的结构体索引法去实现功能与功能之间的来回切换,搭配DHT11,RTC,LED,KEY等器件实现高度智能化一体化操作。后期自己打板设计结构,可以衍生为智能手表等小玩意。目前,项目属于裸机状态(CPU占用率100%),后期可能会加上RTOS系统。(本项目源码在本文末尾进行开源!)
硬件实物图:
效果图:
温度计:
游戏机:
引脚连接:
OLED模块:
VCC --> 3.3V
GND --> GND
SCL --> PB10
SDA --> PB11
DHT11模块:
DATA --> PB9
VCC --> 3.3V
GND --> GND
KEY模块(这部分笔者直接使用了正点原子精英板上的):
KEY0 --> PE4
KEY1 --> PE3
KEY_UP --> PA0
一、多级菜单 随着工业化和自动化的发展,如今基本上所有项目都离不开显示终端。而多级菜单更是终端显示项目中必不可少的组成因素,其实TFT-LCD屏幕上可以借鉴移植很多优秀的开源多级菜单(GUI,比如:LVGL),而0.96寸的OLED屏幕上通常需要自己去适配和编程多级菜单。
精美的多级菜单:
网上的普遍采用的多级菜单的方案是基于索引或者结构树,其中,索引法居多。索引法的优点:可阅读性好,拓展性也不错,查找的性能差不多是最优,就是有点占用内存空间。
说明:本项目的多级菜单也是采用了索引法进行实现。
二、索引法多级菜单实现 网上关于索引法实现多级菜单功能有很多基础教程,笔者就按照本项目中的具体实现代码过程给大家讲解一下索引法实现多级菜单。特别说明:本项目直接使用了正点原子的精英板作为核心板,所以读者朋友复现代码还是很简单的。
首先,基于索引法实现多级菜单的首要条件是先确定项目中将使用到几个功能按键(比如:向前,向后,确定,退出等等)本项目中,笔者使用到了3个按键:下一个(next),确定(enter),退出(back)。所以,接下首先定义一个结构体,结构体中一共有5个变量(3+2),分别为:当前索引序号(current),向下一个(next),确定(enter),退出(back),当前执行函数(void)。其中,标红的为需要设计的按键(笔者这里有3个),标绿的则为固定的索引号与该索引下需要执行的函数。
typedef struct { u8 current; //当前状态索引号 u8 next; //向下一个 u8 enter; //确定 u8 back; //退出 void (*current_operation)(void); //当前状态应该执行的操作 } Menu_table; 接下来就是定义一个数组去决定整个项目菜单的逻辑顺序(利用索引号)
Menu_table table[30]= { {0,0,1,0,(*home)}, //一级界面(主页面) 索引,向下一个,确定,退出 {1,2,5,0,(*Temperature)}, //二级界面 温湿度 {2,3,6,0,(*Palygame)}, //二级界面 游戏 {3,4,7,0,(*Setting)}, //二级界面 设置 {4,1,8,0,(*Info)}, //二级界面 信息 {5,5,5,1,(*TestTemperature)}, //三级界面:DHT11测量温湿度 {6,6,6,2,(*ControlGame)}, //三级界面:谷歌小恐龙Dinogame {7,7,9,3,(*Set)}, //三级界面:设置普通外设状态 LED {8,8,8,4,(*Information)}, //三级界面:作者和相关项目信息 {9,9,7,3,(*LED)}, //LED控制 }; 这里解释一下这个数组中各元素的意义,由于我们在前面先定义了Menu_table结构体,结构体成员变量分别与数组中元素对应。比如:{0,0,1,0,(*home)},代表了索引号为0,按向下键(next)转入索引号为0,按确定键(enter)转入索引号为1,按退出键(back)转入索引号为0,索引号为0时执行home函数。