【Linux】输入系统详述 + 触摸屏应用实战(tslib)

news2024/9/20 12:38:29

目录简述

前言:

一、输入系统

二、Linux输入系统框架

(1)输入系统的驱动层

 (2)输入系统核心层

(3)输入系统事件层

三、APP访问硬件的方式

(1)查询方式、休眠-唤醒

具体示例:

实际效果: 

(2)POLL/SELECT方式

具体示例:

实际效果:

(3)异步通知方式

 具体示例:

实际效果:

四、tslib库框架

(1)电容屏简述

(2)tslib库的用处

(3)tslib框架分析

五、基于tslib的测试程序:

(1)交叉编译tslib库

(2)板子上测试编译

测试结果:

 六、基于tslib的应用实战:

 具体示例:

实际效果:


前言:

经典环节:我一直深信,带着问题思考和实践,能够更容易理解并学习到。

(1)什么是输入系统?

(2)输入设备种类繁多,Linux输入系统是如何管理的?

  • ①繁杂的输入,如何处理成统一标准的输入事件?
  • ②多个事件时,驱动程序上传事件时如何告知APP已完整发送?
  • ③用户程序(APP)是获得数据具体流程是怎么样的?

(3)APP可以以什么样的方式访问硬件?具体又是怎么实现的?

(4)以电容触摸屏为例,用tslib库使用输入设备。

  • tslib库的作用是什么?它有什么优点?
  • tslib库框架是什么样的?内部的机理是什么样的?
  • 如何使用tslib库实现应用程序的功能?---应用实战

接下来的文章内容,将详细的解答上面的问题。如果有所帮助,三连关注( ^_^ ),多多支持一下,大家一同进步呀!

一、输入系统

什么是输入系统?

        我们生活中有很多的输入设备,例如键盘、鼠标、遥控杆、触摸屏等等,用户通过这些设备和Linux系统进行数据交换。

         多个输入设备,是否能统一接口,并也在在驱动层面以及应用层面上统一,Linux系统为了实现上面的需求,设计了一套兼容所有输入设备的框架---即输入系统。

二、Linux输入系统框架

        如下图所示,Linux输入系统管理的方式具体分为三个阶段,①处理成统一标准的输入事件,上发到核心层②接收输入事件,转发给到事件层(event handler)③处理事件,提供用户空间访问接口

        

这里从硬件向上到APP涉及到的概念进行阐述:

(1)输入系统的驱动层

①这里有很多的硬件输入设备,会产生中断,发送数据到系统,那么系统如何去统一的处理, 处理成统一标准的输入事件?

这里的输入事件是一个struct input_event”结构体,查阅Linux内核文件:

D:\Linux-4.9.88.tar\Linux-4.9.88\include\uapi\linux input.h

 

 这里具体输入事件结构统一为time、type、code和value。

timeval结构体---表示事件的发生时间

type:表示哪类事件

code:表示该类事件下的那一个事件

value:表示事件值。

 ②多个事件时,驱动程序上传事件时如何告知APP已完整发送?

驱动程序上报完一系列的数据后,会上报一个“ 同步事件 ”,表示数据上报完毕。  读到“同步事件”时,就知道已经读完了当前的数据。
同步事件也是一个 input_event 结构体,它的 type code value 三项都是 0

 (2)输入系统核心层

中转站的角色,核心层可以决定把输入事件转发给上面哪个 handler 来处理。有多种 handler,比如:evdev_handlerkbd_handlerjoydev_handler 等等。

最常用的是 evdev_handler:它只是把 input_event 结构体保存在内核buffer 等,APP来读取时就原原本本地返回。

(3)输入系统事件层

这里处理核心层上传的输入事件,之后给用户空间提供用户接口。

③在了解系统内部的结构后,用户程序(APP)是获得数据具体流程是怎么样的?

  1. APP发起读操作,若无数据则休眠
  2. 用户操作设备,硬件上产生中断
  3. 输入系统驱动层对应的驱动程序处理中断。
  4. 核心层将输入事件转发到相应的handler处理,最常用的evdev_handler。
  5. APP正在等待数据时,evdev_handler会把它唤醒,这样APP就可以获得数据。

这里APP获得数据的方法有2种,一种是直接访问设备节点,或者通过tslib、libinput这类库来间接访问设备节点。

好用的调试命令:

//查看设备节点,有什么事件

ls /dev/input/* -l

//获取与event对应的相关设备信息

cat /proc/bus/input/devices
//使用命令读取数据(以触摸屏为例)

hexdump /dev/input/event1

三、APP访问硬件的方式

APP可以以什么样的方式访问硬件?

APP访问硬件的方式有四种:查询方式、休眠-唤醒方式、POLL/SELECT方式以及异步通知方式。

方式机理
查询老板时不时来打扰你
休眠-唤醒平时躺着不做事,老板叫了之后干活
POLL/SELECT定个闹钟,时间到了就干活或者老板叫你时干活
异步通知自己在干一些活,老板来叫你时干老板交代的活

上面的方式,不分优劣,都有其应用的场景,那么具体的方法实现是怎么样的?

(1)查询方式、休眠-唤醒

区别
查询方式

APP调用open函数时,传入“O_NONBLOCK”---非阻塞

APP调用read函数时,如果驱动程序中有数据,那么APP的函数会返回数据,否则立刻返回错误。

休眠-唤醒

APP调用open函数时,不传入“O_NONBLOCK”---阻塞

APP调用read函数时,如果驱动程序中有数据,那么APP的函数会返回数据;没有则APP就会在内核态休眠。

具体示例:

#include <linux/input.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <poll.h>


#include <stdio.h>
#include <string.h>




/*01_get_input_info 
	/dev/input/event1	对应触摸屏事件
	O_NONBLOCK(非阻塞方式)
	*/
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	
	struct input_event event;
	
	/*
		Event Type
	*/
	char * ev_names[] ={
		"EV_SYN", 	
		"EV_KEY",	
		"EV_REL",	
		"EV_ABS",	
		"EV_MSC",	
		"EV_SW"	,	
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"EV_LED",	
		"EV_SND",	
		"NUll  ",
		"EV_REP",	
		"EV_FF"	,	
		"EV_PWR",		
	};
	
	//阻塞、非阻塞方式的对比
	if(argc < 2)
	{
		printf("Usage: %s <dev> [noblock]\n",argv[0]);
		return -1;
	}
	if(argc == 3 && !strcmp(argv[2],"noblock"))
	{
		fd =  open(argv[1], O_RDWR | O_NONBLOCK);
	}
	else
	{
		fd =  open(argv[1], O_RDWR);
	}
	if(fd < 0)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
	
	//获取设备的信息
	err = ioctl(fd, EVIOCGID, &id);
	if(err == 0)
	{
		printf("bustype = ox%x\n",id.bustype);
		printf("vendor  = ox%x\n",id.vendor );
		printf("product = ox%x\n",id.product);
		printf("version = ox%x\n",id.version);
	}
	
	//获取evbit,看设备支持什么事件
	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if(len > 0 && len <= sizeof(evbit))
	{
		printf("support evet type: ");
		for(i = 0; i <len; i++)
		{
			byte = ((unsigned char*)evbit)[i];
			for(bit = 0; bit < 8; bit++)
			{
				if(byte & (1 << bit)){
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}
	while(1){
		len = read(fd, &event, sizeof(event));
		if(len == sizeof(event))
		{
			printf(" get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
		}
		else
		{
			printf(" read error %d\n", len);
		}
	}
	return 0;
	
}

实际效果: 

查询方式(非阻塞):

获取设备信息,打开设备节点,之后app读取,驱动程序无数据,会一直return error

 休眠唤醒(阻塞):

获取设备信息,打开设备节点,之后app读取,若无数据,会进入休眠状态,当点击触摸屏时,会返回数据。

(2)POLL/SELECT方式

POLL机制、SELECT机制是完全一样的,只是APP接口函数不一样。

在调用poll、select函数时传入"超时时间"。在这段时间内,条件合适时(比如有数据可读)就会立刻返回,否则等到“超时时间”结束时返回错误。

poll/select监测事件类型有多种,如下表所示:

具体示例:

#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <poll.h>


#include <stdio.h>
#include <string.h>


/*01_get_input_read_poll
	/dev/input/event1	对应触摸屏事件
	*/
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int ret;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	
	struct input_event event;
	struct pollfd fds[1];
	nfds_t nfds = 1;
	
	/*
		Event Type
	*/
	char * ev_names[] ={
		"EV_SYN", 	
		"EV_KEY",	
		"EV_REL",	
		"EV_ABS",	
		"EV_MSC",	
		"EV_SW"	,	
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"EV_LED",	
		"EV_SND",	
		"NUll  ",
		"EV_REP",	
		"EV_FF"	,	
		"EV_PWR",		
	};
	
	
	if(argc != 2)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
	fd =  open(argv[1], O_RDWR);
	if(fd < 0)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
	
	
	//获取设备的信息
	err = ioctl(fd, EVIOCGID, &id);
	if(err == 0)
	{
		printf("bustype = ox%x\n",id.bustype);
		printf("vendor  = ox%x\n",id.vendor );
		printf("product = ox%x\n",id.product);
		printf("version = ox%x\n",id.version);
	}
	
	//获取evbit,看设备支持什么事件
	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if(len > 0 && len <= sizeof(evbit))
	{
		printf("support evet type: ");
		for(i = 0; i <len; i++)
		{
			byte = ((unsigned char*)evbit)[i];
			for(bit = 0; bit < 8; bit++)
			{
				if(byte & (1 << bit)){
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}
	//POLL和SELECT方式读取输入数据,超时时间-5s
	while(1){
        //想查询哪个文件(fd)
		fds[0].fd = fd;
        //想查询什么事件
		fds[0].events = POLLIN;
        //清除“返回事件”
		fds[0].revents = 0;
		ret = poll(fds, nfds, 5000);
		if(ret > 0){
			if(fds[0].revents == POLLIN)
			{
				while(read(fd, &event, sizeof(event)) == sizeof(event))
				{
					printf(" get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
				}
			}
		}
		else if(ret == 0)
		{
			printf("time out\n");
		}
		else
		{
			printf("poll err\n");
		}
	}
	return 0;

实际效果:

(3)异步通知方式

异步通知机制是类似与单片机开发的中断。就是APP可以忙自己的事,当驱动程序有数据时它会主动给APP发信号,这时APP执行信号处理函数。

除了清楚上述简要的流程以及对象,还有一些具体问题需要解决。

  1. 驱动程序给APP发什么信号?----SIGIO(驱动常用信号)
  2. 怎么发信号?                         ---内核提供函数
  3. 信号处理函数和信号之间怎么挂钩:APP注册信号处理函数

①...\Linux-4.9.88\include\uapi\asm-generic signal.h中有很多信号:

 ②APP提供注册信号处理函数的同时,也要跟SIGIO挂钩,具体如法如下:

进一步的思考:

  • 内核有很多驱动,让哪一个驱动给app发SIGIO信号?
    • APP打卡驱动程序的设备节点
  • 驱动程序怎么知道要将发信号给现在这个APP?
    • APP把自己进程ID告诉驱动程序

 具体示例:

#include <linux/input.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h> 
#include <sys/types.h>
#include <fcntl.h>


#include <stdio.h>
#include <string.h>


int fd;

//信号处理函数
void sig_func_handler(int sig)
{
	struct input_event event;
	while(read(fd, &event, sizeof(event)) == sizeof(event))
	{
		printf(" get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
	}

	
};
/*05_get_read_faycn 
	/dev/input/event1	对应触摸屏事件
	*/
int main(int argc, char **argv)
{
	int err;
	int len;
	int ret;
	int i;
	int flags;
	int count = 0;
	
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	
	
	/*
		Event Type
	*/
	char * ev_names[] ={
		"EV_SYN", 	
		"EV_KEY",	
		"EV_REL",	
		"EV_ABS",	
		"EV_MSC",	
		"EV_SW"	,	
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"NUll  ",
		"EV_LED",	
		"EV_SND",	
		"NUll  ",
		"EV_REP",	
		"EV_FF"	,	
		"EV_PWR",		
	};
	
	
	if(argc != 2)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
	/*注册信号处理函数*/
	signal(SIGIO, sig_func_handler);

	/*打开驱动程序*/
	fd =  open(argv[1], O_RDWR);
	if(fd < 0)
	{
		printf("Usage: %s <dev>\n",argv[0]);
		return -1;
	}
			
	//获取设备的信息
	err = ioctl(fd, EVIOCGID, &id);
	if(err == 0)
	{
		printf("bustype = ox%x\n",id.bustype);
		printf("vendor  = ox%x\n",id.vendor );
		printf("product = ox%x\n",id.product);
		printf("version = ox%x\n",id.version);
	}
	
	//获取evbit,看设备支持什么事件
	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if(len > 0 && len <= sizeof(evbit))
	{
		printf("support evet type: ");
		for(i = 0; i <len; i++)
		{
			byte = ((unsigned char*)evbit)[i];
			for(bit = 0; bit < 8; bit++)
			{
				if(byte & (1 << bit)){
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}
	
	/*把APP的进程号告诉驱动程序*/
	fcntl(fd, F_SETOWN, getpid());
	
	/*使能"异步通知"*/
	flags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, flags | FASYNC);
	
	while(1){
		printf("main loop count = %d\n", count++);
		sleep(2);
	}
	return 0;
	
}

实际效果:

四、tslib库框架

(1)电容屏简述

电容屏中有一个控制芯片,它会周期性产生驱动信号,接收电极接收到信号,并可测量电荷大小。当电容屏被按下时,相当于引入新的电容,从而影响了接收电极接收到的电荷大小。主控芯片根据电荷大小即可计算触点位置。

电容触摸屏数据分析,对开发板上电容屏对应的设备节点/dev/input/event1,执行以下的命令:

hexdump /dev/input/event1

①一个手指点击触摸屏得到的:

② 两个手指点击触摸屏得到的:

(2)tslib库的用处

tslib库的作用是什么?它有什么优点?

我们可以看到,点击触摸屏时会有很多的事件,我们去做过滤和处理是不方便的。

tslib是一个触摸屏的开源库,可以使用它来访问触摸屏设备,可以给输入设备添加各种“filter”(过滤库,就是各种处理)。

(3)tslib框架分析

tslib库框架是什么样的?内部的机理是什么样的?

tslib的框架如图所示:

 tslib的主要代码有:

  • src/ 接口函数
    • ts_setup.c
    • ts_open.c
    • ts_config.c
  • plugins/ 模块module,以下的都是一个模块
    • linear.c
    • dejitter.c
    • pthres.c
    • input_raw.c     
  • tests/ 测试程序
    • ts_test.c
    • ts_test_mt.c
    • ts_print.c
    • ts_print_mt.c

分析整个tslib框架,参照示例程序(ts_test.cts_test_mt.c),用于单点触摸屏以及多点触摸屏。tslib的运行流程如下:

  • 1.调用ts_open,打开设备节点,构造出tsdev结构体。这个结构体的内容如下:
  • 2.调用ts_config,读取配置文件的处理,对于所有的模块,都会插入链表表头。module和module_raw对应tsdev结构体里不同的链表list、list_raw。
  • 3.递归调用各个模块,input_raw→pthres→dejitter→linear模块,对从设备节点获得的原始数据进行数据处理,返回最终数据。下图就是递归的过程。

所以主体就调用三个函数:ts_setup、ts_read和ts_readmt、ts_close。

五、基于tslib的测试程序:

如何使用tslib库实现应用程序的功能?

(1)交叉编译tslib库

//配置交叉编译工具链复制时注意检查哈,看是否一致)

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin

//tslib库解压

cp /home/book/01_all_series_quickstart/04_嵌入式Linux应用开发基础知识/source/11_input/02_tslib/tslib-1.21.tar.xz ./

tar xJf tslib-1.21.tar.xz

//交叉编译万能命令

cd tslib-1.21

./configure --host=arm-linux-gnueabihf --prefix=/

make

make install DESTDIR=$PWD/tmp

//把头文件、库文件放到工具链目录下

cd tslib-1.21/tmp/

cp include/* /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include

cp -d lib/*so* /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/

(2)板子上测试编译

//复制文件到nfs挂载的文件夹nfs_rootfs里

cp -r  ~/tslib-1.21/*  ~/nfs_rootfs

//板子上将文件复制到lib、bin和etc上

cp /mnt/tslib-1.21/tmp/lib/*so* -d /lib

cp /mnt/tslib-1.21/tmp/bin/* /bin

cp /mnt/tslib-1.21/tmp/etc/ts.conf -d /etc

cp /mnt/tslib-1.21/tmp/lib/ts -rf /lib

//关闭默认的qt GUI程序(以实际为准),打开/etc/init.d/查看 qtGUI程序名

//重新开启的话,就将将相应的文件移回去

mv /etc/init.d/S99myirhmi2 /root

reboot

//测试

ts_test_mt

测试结果:

 六、基于tslib的应用实战:

 实现一个程序,不断打印2个触点的距离

触摸屏可能支持多个触点,比如5个,tslib为了简化处理,即使只有两个触点,ts_read_mt函数也会返回五个触点的数据。

驱动程序中使用slot、tracing_id来标识一个触点,当tracing_id等于-1时,标识这个触点被松开了。

所以可以根据这个标识来判断数据是否有效,所以当有两个触点有效时,就打印它俩之间的距离。

核心函数:ts_read_mt

三个参数:tsdev结构体、max_slots(最大点数)、ts_sample_mt结构体(存数据)

 

 具体示例:

根据第四部分的内容、上述的思路以及ts_test_mt.c示例程序,完成程序的编写。

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>

#include <linux/input.h>

#include <sys/ioctl.h>

#include <tslib.h>

int distance(struct ts_sample_mt *point1, struct ts_sample_mt *point2)
{
	int x = point1->x - point2->x;
	int y = point1->y - point2->y;

	return x*x + y*y;
}

int main(int argc, char **argv)
{
	struct tsdev *ts;
	int i;
	int ret;
	//定义新旧触点sample结构体
	struct ts_sample_mt **samp_mt;
	struct ts_sample_mt **pre_samp_mt;
	int max_slots;
	int point_pressed[20];
	struct input_absinfo slot;
	int touch_cnt = 0;

	//阻塞方式
	ts = ts_setup(NULL, 0);
	if (!ts)
	{
		printf("ts_setup err\n");
		return -1;
	}
	
	//读取设备节点,获取属性---同时支持多少个触点,得到max_slots
	if (ioctl(ts_fd(ts), EVIOCGABS(ABS_MT_SLOT), &slot) < 0) {
		perror("ioctl EVIOGABS");
		ts_close(ts);
		return errno;
	}

	max_slots = slot.maximum + 1 - slot.minimum;

	
	//参照测试程序初始samp_mt和pre_samp_mt结构体数组
	samp_mt = malloc(sizeof(struct ts_sample_mt *));
	if (!samp_mt) {
		ts_close(ts);
		return -ENOMEM;
	}
	samp_mt[0] = calloc(max_slots, sizeof(struct ts_sample_mt));
	if (!samp_mt[0]) {
		free(samp_mt);
		ts_close(ts);
		return -ENOMEM;
	}

	pre_samp_mt = malloc(sizeof(struct ts_sample_mt *));
	if (!pre_samp_mt) {
		ts_close(ts);
		return -ENOMEM;
	}
	pre_samp_mt[0] = calloc(max_slots, sizeof(struct ts_sample_mt));
	if (!pre_samp_mt[0]) {
		free(pre_samp_mt);
		ts_close(ts);
		return -ENOMEM;
	}

	for ( i = 0; i < max_slots; i++)
		pre_samp_mt[0][i].valid = 0;

	

	while (1)
	{
		//第一步:读取触点数据
		ret = ts_read_mt(ts, samp_mt, max_slots, 1);
		if (ret < 0) {
			printf("ts_read_mt err\n");
			ts_close(ts);
			return -1;
		}

		//第二步:判断是否更新,将新数据拷贝到旧数据里
		for (i = 0; i < max_slots; i++)
		{
			if (samp_mt[0][i].valid)
			{
				memcpy(&pre_samp_mt[0][i], &samp_mt[0][i], sizeof(struct ts_sample_mt));
			}
		}

		//第三步:判断是否有两个触点,如果是两个,则执行打印
		touch_cnt = 0;
		for (i = 0; i < max_slots; i++)
		{
			if (pre_samp_mt[0][i].valid && pre_samp_mt[0][i].tracking_id != -1)
				point_pressed[touch_cnt++] = i;
		}

		if (touch_cnt == 2)
		{
			printf("distance: %08d\n", distance(&pre_samp_mt[0][point_pressed[0]], &pre_samp_mt[0][point_pressed[1]]));
		}
	}
	
	return 0;
}

实际效果:

//交叉编译
arm-buildroot-linux-gnueabihf-gcc -o mt_cal_distance mt_cal_distance.c -lts

//复制到nfs挂载文件 nfs_rootfs
cp mt_cal_distance ~/nfs_rootfs

板子上运行程序,两个手指放上去之后打印出结果: 

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

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

相关文章

Linux环境下安装RocketMQ

目录 前置要求&#xff1a; 一、下载RocketMQ 二、上传解压 三、配置rocketmq的环境变量 四、查看rocketmq的目录结构 五、启动 5.1 启动nameserver 5.2 启动broker 六、测试发送消息 七、关闭 前置要求&#xff1a; 准备一台Linux系统的虚拟机提前安装jdk1.8 不会…

C++函数重载的简单介绍

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】 中华文化博大精深&#xff0c;我们知道在我们的汉语中&#xff0c;每个词语都有着其不同的含义&#xff0c;甚至是一个词语中有…

毫米波雷达将被颠覆?楚航科技发布隐形雷达ART

4月19日上海车展现场&#xff0c;楚航科技首次对外展示最新的前瞻性研发第N代创新产品——隐形雷达ART。 楚航科技本次发布的科技隐形雷达ART&#xff0c;打破一体式封装设计&#xff0c;重新定义车载毫米波雷达物理形态&#xff0c;为行业提供全新的颠覆性产品设计新路线。 资…

LeetCode 1000. Minimum Cost to Merge Stones【记忆化搜索,动态规划,数组】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

【C++类和对象】类和对象(中):析构函数 {析构函数的概念及特性,编译器自动生成的析构函数,构造析构的顺序}

三、析构函数 3.1 概念 通过前面构造函数的学习&#xff0c;我们知道一个对象是怎么来的&#xff0c;那一个对象又是怎么没呢的&#xff1f; 析构函数&#xff1a;与构造函数功能相反&#xff0c;析构函数不是完成对对象本身的销毁&#xff0c;局部对象随函数栈帧的销毁而销毁…

chatGpt自动写文章-chatGpt自动写文章软件

怎么用GPT写文章 使用GPT写文章需要按照以下步骤进行&#xff1a; 确定文章主题&#xff1a;首先需要明确文章的主题&#xff0c;这有助于GPT更好地了解你想要表达的内容&#xff0c;并生成更有针对性的文本。 准备开头和结尾&#xff1a;根据文章主题&#xff0c;准备好文章开…

【参考文献不爆红】Word的多个参考文献连续交叉引用([1] [3]改为[1-3])

文章目录 1. 参考文献格式2. 引入参考文献3. Word的多个参考文献连续交叉引用&#xff08;[1] [3]改为[1-3]&#xff09;3.1引入两个参考文献3.2 引入三个参考文献3.3 知识科普 1. 参考文献格式 参考教程 全选参考文献–>编号的小三角–>自定义编号&#xff0c;修改为[]…

PostMan笔记(三)自动化测试

1. 简介 Postman是一款功能强大的API开发工具&#xff0c;也是一款流行的自动化测试工具。它提供了多种测试功能&#xff0c;包括测试脚本、预请求脚本和测试集合等。 1.1 测试脚本 测试脚本是Postman中用于自动化测试的核心部分。它可以使用JavaScript语言编写&#xff0c;…

使用VScode编写C语言程序 环境安装配置 保姆级教程

Visual Studio Code可通过安装插件来支持C、C#、Python、PHP等语言&#xff0c;使用的工程师越来越多&#xff0c;本文介绍如何使用VS Code进行C语言的编译与调试 目录 一 vsCode配置C/C环境 1. vsCode下载和安装 2. 安装vsCode 二 MinGW编译器下载和配置 1. 下载编译器M…

c++积累8-右值引用、移动语义

1、右值引用 1.1 背景 c98中的引用很常见&#xff0c;就是给变量取个别名&#xff0c;具体可以参考c积累7 在c11中&#xff0c;增加了右值引用的概念&#xff0c;所以c98中的引用都称为左值引用 1.2 定义 右值引用就是给右值取个名字&#xff0c;右值有了名字之后就成了普通…

【达摩院OpenVI】基于流感知的视频目标检测网络LongShortNet

论文&代码 论文链接&#xff1a;[arxiv]代码&应用&#xff1a; 开源代码&#xff1a;[github code]开源应用&#xff1a;[modelscope] 背景介绍 传统视频目标检测&#xff08;Video Object Detection, VOD&#xff09;任务以一段视频作为输入&#xff0c;利用视频的…

项目上线|慕尚集团携手盖雅工场,用数字化推动人效持续提升

过去十年&#xff0c;中国零售业以前所未有的速度被颠覆、被重塑&#xff0c;数字化则是其中重要的推动要素。 随着数字化转型的深入&#xff0c;零售企业的数字化不再局限于布局线上渠道&#xff0c;且更关乎其背后企业核心运营能力的全链路数字化改造。而贯穿于运营全链路的…

mybatis缓存的详细理解和使用

mybatis缓存的简单理解和使用 mybatis缓存数据的介绍 缓存是存在于内存中的临时数据&#xff0c;使用缓存的目的是减少和数据库的数据进行交互的次数&#xff0c;提高执行效率。像很多持久化框架一样&#xff0c;Mybatis也提供了缓存策略&#xff0c;通过缓存策略来减少数据库…

RflySim平台使用篇 | Coptersim系列教程(三)

# 导读 # CopterSim作为RflySim平台核心仿真软件&#xff0c;其主要实现两部分功能&#xff1a;模型和通信&#xff0c;掌握CopterSim使用方法即可轻松运行多旋翼运动动态模型&#xff0c;并连同其他软件构成软/硬件在环仿真。本篇教程将详细介绍coptersim仿真log数据获取。 Co…

webpack plugin源码解析(六) CompressionWebpackPlugin

文章目录 作用涉及 webpack API处理 asset 钩子compilation.hooks.processAssets返回或新建缓存&#xff1a;compilation.getCache返回 asset 文件信息&#xff1a;compilation.getAsset文件名匹配函数&#xff1a;compiler.webpack.ModuleFilenameHelpers.matchObject模版字符…

科研热点|8本期刊被剔除SCIE,4月最新SCIE/SSCI目录已更新 (附下载)~

2023年4月18日&#xff0c;科睿唯安更新了Web of Science核心期刊目录&#xff0c;此次更新后SCIE期刊目录共包含9505本期刊&#xff0c;SSCI期刊目录共包含3557本期刊。此次4月SCIE & SSCI期刊目录更新&#xff0c;与3月更新相比 (警惕&#xff01;多达50本SCI/SSCI被剔除…

Kafka中时间轮分析与Java实现

仿kafka实现java版时间轮_java实现时间轮算法_Hekliu的博客-CSDN博客 https://www.cnblogs.com/softlin/p/7426083.html https://blog.csdn.net/happyjacob/article/details/128518700 一、背景 在Kafka中应用了大量的延迟操作但在Kafka中 并没用使用JDK自带的Timer或是Dela…

m3u8转mp4下载,有URL,IV

1、背景 在线m3u8现在是主流加密方式的视频。 2、下载m3u8视频难点 首先需要连接m3u8文件格式,这个自行百度,其次加密方式确定和key以及iv。如果没有加密直接找一个在线的m3u8转mp4就可以,但是问题就是很多带加密,而且key不是m3m8中key URL返回的数据,市面上软件无法直…

基于matlab评估机场监控雷达上 5G 新无线电 (NR) 信号的干扰

一、前言 随着5G NR系统的频率范围超出LTE中使用的频段&#xff0c;频谱管理变得更加复杂。对扩大5G覆盖范围的需求是由更高的数据速率和更低的延迟的好处推动的。新5G基站的实施反过来又推动了了解这些信号如何影响在相同频段上运行的已安装系统的需求。其中一个系统是空中交通…

类对象

一、类初识 类&#xff1a;表示一种事物所具有的共同特征和行为 对象&#xff1a;一个类的实例 如下图&#xff0c;通过狗这个类进行详解 这是一个Dog类 对象&#xff1a;斗牛犬、小猎犬、牧羊犬 类中的属性&#xff1a;breed(品种)、size(大小)、color(颜色)、age(年龄)、 …