Linux基础项目开发1:量产工具——输入系统(三)

前言:

前面我们已经实现了显示系统,现在我们来实现输入系统,与显示系统类似,下面让我们一起来对输入系统进行学习搭建吧

目录

一、数据结构抽象

1. 数据本身

2. 设备本身:

3. input_manager.h

二、触摸屏编程 

touchscreen.c 

三、触摸屏单元测试 

1.touchscreen.c 

2.上机测试

四、网络编程 

netiput.c 

五、网络单元测试

1.netiput.c 

2.client.c

3.上机测试 


、数据结构抽象

        对于每一个设备,每一个模块,都用一个结构体来表示它,以后就会很方便的替换这些模块,所以对于设备本身我们需要抽象出一个结构体,所以对于输入系统我们需要抽象出两个结构体,这两个结构分别是:1.数据本身、2.设备本身。

输入来源:1.网络数据输入   2.点击事件输入 

1. 数据本身

通过这个数据抽象,就可以既表示出触摸屏本身的数据也能表示出网络本身的数据

触摸屏数据:

int iType :           判断是什么数据,是触摸屏数据还是网络数据

int iX、int iY:        判断具体坐标

int iPressure:     压力值

网络数据: 

char str[1024]:   网络数据

2. 设备本身:

 *name:                            设备名字

(*GetInputEvent)(PInputEvent ptInputEvent):        上层代码通过这个函数得到数据,返回值判断数据是否成功,如果成功数据保存至PInputEvent ptInputEvent中

(*DeviceInit):                 提供初始化函数,比如打开设备节点等

(*DeviceExit):                提供退出函数

InputDevice *ptNext :   如果想支持多个设备,就应该将这些设备列到一起,所以需要一个链表指针

3. input_manager.h

#ifndef _INPUT_MANAGER_H
#define _INPUT_MANAGER_H

#include <sys/time.h>

#ifndef NULL
#define NULL (void *)8#endif


#define INPUT_TYPE_TOUCH 1
#define INPUT_TYPE_NET   2


typedef struct InputEvent {
	struct timeval	tTime;
	int iType;
	int iX;
	int iY;
	int iPressure;
	char str[1024];
}InputEvent, *PInputEvent;


typedef struct InputDevice {
	char *name;
	int (*GetInputEvent)(PInputEvent ptInputEvent);
	int (*DeviceInit)(void);
	int (*DeviceExit)(void);
	struct InputDevice *ptNext;
}InputDevice, *PInputDevice;

void RegisterInputDevice(PInputDevice ptInputDev);
void InputInit(void);

#endif

第9行:触摸屏事件

第10行:网络事件

第13~20行:抽象出数据本身结构体InputEvent

        第14行:定义出一个时间

第23~29行:抽象出设备本身结构体InputDevice 

第32行:用于输入系统管理,提供一个注册函数,下面的各个设备进行引用它

第33行:用注册函数进行引用

二、触摸屏编程 

设备结构体的实现如下结构体:使用tslib

touchscreen.c 

#include <input_manager.h>
#include <tslib.h>
#include <stdio.h>
static struct tsdev *g_ts;

static int TouchscreenGetInputEvent(PInputEvent ptInputEvent)
{
	struct ts_sample samp;
	int ret;
	
	ret = ts_read(g_ts, &samp, 1);
	
	if (ret != 1)
		return -1;

	ptInputEvent->iType     = INPUT_TYPE_TOUCH;
	ptInputEvent->iX        = samp.x;
	ptInputEvent->iY        = samp.y;
	ptInputEvent->iPressure = samp.pressure;
	ptInputEvent->tTime     = samp.tv;

	return 0;
}

static int TouchscreenDeviceInit(void)
{
	g_ts = ts_setup(NULL, 0);
	if (!g_ts)
	{
		printf("ts_setup err\n");
		return -1;
	}

	return 0;
}

static int TouchscreenDeviceExit(void)
{
	ts_close(g_ts);
	return 0;
}


static InputDevice g_tTouchscreenDev ={
	.name = "touchscreen",
	.GetInputEvent  = TouchscreenGetInputEvent,
	.DeviceInit     = TouchscreenDeviceInit,
	.DeviceExit     = TouchscreenDeviceExit,
};


void TouchscreenRegister(void)
{
	TouchscreenRegister(&g_tTouchscreenDev);
}

第4行:初始化结果放到全局变量g_ts

第8行:定义一个ts_sample 的结构体samp

static int TouchscreenGetInputEvent(PInputEvent ptInputEvent)

 第6~23行:获得输入事件

static int TouchscreenDeviceInit(void)

第25~35行:对设备的初始化

static int TouchscreenDeviceExit(void)

第37~41行:关闭设备

void TouchscreenRegister(void)

第52~55行:将结构体g_tTouchscreenDev注册到上一层,上一层就是输入管理器,用注册函数进行引用

 

三、触摸屏单元测试 

1.touchscreen.c 

在touchscreen.c 代码下面加一个main函数进行触摸屏的单元测试


#if 1

int main(int argc, char **argv)
{
	InputEvent event;
	int ret;
	
	g_tTouchscreenDev.DeviceInit();

	while (1)
	{
		ret = g_tTouchscreenDev.GetInputEvent(&event);
		if (ret) {
			printf("GetInputEvent err!\n");
			return -1;
		}
		else
		{
			printf("Type      : %d\n", event.iType);
			printf("iX        : %d\n", event.iX);
			printf("iY        : %d\n", event.iY);
			printf("iPressure : %d\n", event.iPressure);
		}
	}
	return 0;
}

#endif

注意:#if 1则执行,# if 0则不执行

第5行:定义一个InputEvent 结构体 event 获取数据本身传给GetInputEvent()

第8行:调用g_tTouchscreenDev结构体中的DeviceInit()进行初始化

第12行:调用g_tTouchscreenDev结构体中的GetInputEvent(&event)进行获取输入事件

第17~23行:如果返回值ret=0的话,则将数据信息打印出来

EXTRA_CFLAGS  := 
CFLAGS_file.o := 

#obj-y += disp_test.o

将unittest文件夹中的Makefile中的main函数部分注释掉,因为一个程序只能允许有一个main

EXTRA_CFLAGS  := 
CFLAGS_file.o := 

obj-y += touchscreen.o

input目录下的Makefile


CROSS_COMPILE ?= 
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nm

STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump

export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP

CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include

LDFLAGS := -lts

export CFLAGS LDFLAGS

TOPDIR := $(shell pwd)
export TOPDIR

TARGET := test


obj-y += display/
obj-y += input/

all : start_recursive_build $(TARGET)
	@echo $(TARGET) has been built!

start_recursive_build:
	make -C ./ -f $(TOPDIR)/Makefile.build

$(TARGET) : built-in.o
	$(CC) -o $(TARGET) built-in.o $(LDFLAGS)

clean:
	rm -f $(shell find -name "*.o")
	rm -f $(TARGET)

distclean:
	rm -f $(shell find -name "*.o")
	rm -f $(shell find -name "*.d")
	rm -f $(TARGET)
	

需要修改顶层目录下的Makefile

第20行:LDFLAGS := -lts 设置链接

第31行:打开input目录下的文件

2.上机测试

进行make编译,如果出现以下情况,则重新译一下tslib

 上板效果如下:点击开发板会将点击的信息显示出来

四、网络编程 

对网络输入构造出同触摸屏编程中一样的结构体

netiput.c 



#include <input_manager.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>


/* socket
 * bind
 * sendto/recvfrom
 */

#define SERVER_PORT 8888

static int g_iSocketServer;

static int NetinputGetInputEvent(PInputEvent ptInputEvent)
{
	struct sockaddr_in tSocketClientAddr;
	struct int iRecvLen;
	char aRecvBuf[1000];
	
	int iAddrLen = sizeof(struct sockaddr);
	
	iRecvLen = recvfrom(g_iSocketServer, aRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
	if (iRecvLen > 0)
	{
		aRecvBuf[iRecvLen] = '\0';
		//printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
		ptInputEvent->iType 	= INPUT_TYPE_NET;
		gettimeofday(&ptInputEvent->tTime, NULL);
		strncpy(ptInputEvent->str, aRecvBuf, 1000);
		ptInputEvent->str[999] = '\0';
		return 0;
	}
	else
		return -1;
}

static int NetinputDeviceInit(void)
{
	struct sockaddr_in tSocketServerAddr;
	int iRet;

	
	
	g_iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == g_iSocketServer)
	{
		printf("socket error!\n");
		return -1;
	}

	tSocketServerAddr.sin_family      = AF_INET;
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
	memset(tSocketServerAddr.sin_zero, 0, 8);
	
	iRet = bind(g_iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
	if (-1 == iRet)
	{
		printf("bind error!\n");
		return -1;
	}

	return 0;
}

static int NetinputDeviceExit(void)
{
	close(g_iSocketServer);	
	return 0;
}


static InputDevice g_tNetinputDev ={
	.name = "touchscreen",
	.GetInputEvent  = NetinputGetInputEvent,
	.DeviceInit     = NetinputDeviceInit,
	.DeviceExit     = NetinputDeviceExit,
};


void NetInputRegister(void)
{
	NetInputRegister(&g_tNetinputDev);
}

第29行:端口

第22行:获得的数据  

第28行:获得的数据保存到ucRecvBuf里面

static int NetinputGetInputEvent(PInputEvent ptInputEvent)

   第24~45行:获得输入事件

static int NetinputDeviceInit(void)

   第47~74行:初始化socket

static int NetinputDeviceExit(void)

 第76~80行:关闭设备

void NetInputRegister(void)

第88~91行:将结构体g_tNetinputDev注册到上一层,上一层就是输入管理器,用注册函数进行引用

 

五、网络单元测试

1.netiput.c 

这里充当服务器端,用来接收数据


#if 1

int main(int argc, char **argv)
{
	InputEvent event;
	int ret;
	
	g_tNetinputDev.DeviceInit();

	while (1)
	{
		ret = g_tNetinputDev.GetInputEvent(&event);
		if (ret) {
			printf("GetInputEvent err!\n");
			return -1;
		}
		else
		{
			printf("Type      : %d\n", event.iType);
			printf("str       : %s\n", event.str);
		}
	}
	return 0;
}

#endif

注意:需要把触摸屏里面的main函数注释掉

 Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := 

obj-y += touchscreen.o
obj-y += netinput.o

2.client.c

这里充当客户端,用来发送数据

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>

/* socket
 * connect
 * send/recv
 */

#define SERVER_PORT 8888

int main(int argc, char **argv)
{
	int iSocketClient;
	struct sockaddr_in tSocketServerAddr;
	
	int iRet;
	int iSendLen;
	int iAddrLen;

	if (argc != 3)
	{
		printf("Usage:\n");
		printf("%s <server_ip> <str>\n", argv[0]);
		return -1;
	}

	iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);

	tSocketServerAddr.sin_family      = AF_INET;
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
 	if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
 	{
		printf("invalid server_ip\n");
		return -1;
	}
	memset(tSocketServerAddr.sin_zero, 0, 8);

#if 0
	iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));	
	if (-1 == iRet)
	{
		printf("connect error!\n");
		return -1;
	}
#endif

	iAddrLen = sizeof(struct sockaddr);
	iSendLen = sendto(iSocketClient, argv[2], strlen(argv[2]), 0,
	              (const struct sockaddr *)&tSocketServerAddr, iAddrLen);

	close(iSocketClient);
	
	return 0;
}

arm-buildroot-linux-gnueabihf-gcc -o client unittest/client.c
make
cp test ~/nfs_rootfs/

[root@100ask:~]# cd /mnt/

3.上机测试 

六、 输入系统的框架

         如果只有一个输入设备,那么我们只需要写一个main函数即可,但是我们需要好几个设备输入,那么要想统一管理,那么就需要设计一个输入管理系统进行管理选择到底哪个设备进行输入。

        在输入管理器中我们需要初始化触摸屏,并且为触摸屏提供一个线程,初始化网络设备,为网络设备提供一个线程,上层应用程序提供一些函数就可以方便的得到各类的输入事件,线程的创建线程的管理都由输入管理器进行设置。

 1.input_manager.c

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>

static PInputDevice g_InputDevs  = NULL;

void RegisterInputDevice(PInputDevice ptInputDev)
{
	ptInputDev->ptNext = g_InputDevs;
	g_InputDevs = ptInputDev;
}

/*  */
void InputInit(void)
{
	/* regiseter touchscreen */
	extern void TouchscreenRegister(void);
	TouchscreenRegister();

	/* regiseter netinput */
	extern void NetInputRegister(void);
	NetInputRegister();
}

static void *input_recv_thread_func (void *data)
{
	PInputDevice tInputDev = (PInputDevice)data;
	InputEvent tEvent;
	int ret;
	
	while (1)
	{
		/* 读数据 */
		ret = tInputDev->GetInputEvent(&tEvent);

		if (!ret)
		{	
			/* 保存数据 */

			/* 唤醒等待数据的线程 */
		
			pthread_mutex_lock(&g_tMutex);
			pthread_cond_wait(&g_tConVar, &g_tMutex);	

			pthread_mutex_unlock(&g_tMutex);
		}
	}

	return NULL;
}

void IntpuDeviceInit(void)
{
	int ret;
	pthread_t tid;
	
	/* for each inputdevice, init, pthread_create */
	PInputDevice ptTmp = g_InputDevs;
	while (ptTmp)
	{
		/* init device */
		ret = ptTmp->DeviceInit();

		/* pthread create */
		if (!ret)
		{
			ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp);
		}

		ptTmp= ptTmp->ptNext;
	}
}

int GetInputEvent(PT_InputEvent ptInputEvent)
{
	/* 无数据则休眠 */

	/* 返回数据 */
}



第7行:这一层要接受下一层传输上来的注册信息 ,所以要定义一个 g_InputDevs的链表头

void RegisterInputDevice(PInputDevice ptInputDev)

第9~13行:提供一个注册函数

void InputInit(void)

第16~25行:向上提供一个InputInit(void) 函数

        第19、20行:注册触摸屏设备

        第23、24行:注册网络设备

注意:其中extern是外部函数的意思

void IntpuDeviceInit(void)

第54~74行:对于每个设备初始化它,并且创建线程

int GetInputEvent(PT_InputEvent ptInputEvent)

第76行:最重要的一个函数 ,最上层的代码只要调用这个函数就可以得到这些设备的数据

         

怎么避免数据丢失 ?

比如触摸屏,它会一下子上报很多数据

对于网络输入,也有可能同时又多个client发来数据

所以,不能使用单一的变量来保存数据,而是使用一个数组来保存数据

使用“环形缓冲区

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

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

相关文章

云时空社会化商业 ERP 系统 gpy 文件上传漏洞复现

0x01 产品简介 时空云社会化商业ERP&#xff08;简称时空云ERP&#xff09; &#xff0c;该产品采用JAVA语言和Oracle数据库&#xff0c; 融合用友软件的先进管理理念&#xff0c;汇集各医药企业特色管理需求&#xff0c;通过规范各个流通环节从而提高企业竞争力、降低人员成本…

VSCODE 在新窗口中打开

使用VS习惯了&#xff0c;经常在新窗口中打开查看 但是VSCODE&#xff0c;无法拖动标签到一个新窗口中&#xff0c;一直以为没这个功能 后来发现 使用快捷健 ctlk,o 可以将标签页在新窗口中打开&#xff0c;虽然不如vsstudio方便&#xff0c;不过也可实现在新窗口打开的功能…

【动态规划】LeetCode2552:优化了6版的1324模式

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 动态规划 本题其它解法 C前缀和算法的应用&#xff1a;统计上升四元组 类似题解法 包括题目及代码C二分查找算法&#xff1a;132 模式解法一枚举3C二分查找算法&am…

CSAPP bomb_lab:phase_5

phase_5的汇编代码 0x0000000000401062 <0>: push %rbx0x0000000000401063 <1>: sub $0x20,%rsp0x0000000000401067 <5>: mov %rdi,%rbx0x000000000040106a <8>: mov %fs:0x28,%rax0x0000000000401073 <17>: mov …

hql面试题之字符串使用split分割,并选择其中的一部分字段的问题

版本&#xff1a;20231109 1.题目&#xff1a; 有两张表,a表有id和abstringr两个字段&#xff0c;b表也有id和bstr两个字段&#xff0c;具体如下 A表&#xff1a; 1abc,bcd,cdf2123,456,789 B表: 1acddef2123456 在a表的abstring字段中用‘,’分割&#xff0c;并取出前两…

sqli-labs靶场详解(less32-less37)

宽字节注入 原理在下方 目录 less-32 less-33 less-34 less-35 less-36 less-37 less-32 正常页面 ?id1 下面有提示 获取到了Hint: The Query String you input is escaped as : 1\ ?id1 看来是把参数中的非法字符就加上了转义 从而在数据库中只能把单引号当成普通的字…

Go 从编译到执行

一、Go运行编译简介 Go语言&#xff08;也称为Golang&#xff09;自从2009年由Google发布以来&#xff0c;已成为现代软件开发中不可或缺的一部分。设计者Rob Pike, Ken Thompson和Robert Griesemer致力于解决多核处理器、网络系统和大型代码库所引发的现实世界编程问题。我们…

TA-Lib学习研究笔记——Overlap Studies(二)上

TA-Lib学习研究笔记——Overlap Studies&#xff08;二&#xff09; 1. Overlap Studies 指标 [BBANDS, DEMA, EMA, HT_TRENDLINE, KAMA, MA, MAMA, MAVP, MIDPOINT, MIDPRICE, SAR, SAREXT, SMA, T3, TEMA, TRIMA, WMA]2.数据准备 get_data函数参数&#xff08;代码&#x…

msyql迁移到mongodb

关系型数据库迁移到mongodb的理由 高并发需求&#xff0c;关系型数据库不容易扩展 快速迭代 灵活的json模式 大数据量需求 应用迁移难度&#xff1a; 关系型到关系 oracle-》mysql oracle -》 postgresql 关系到文档- oracle -》 mongodb 需要考虑&#xff1a; 总体架构&#…

阿里云Windows server2016 安装Docker

阿里云Windows server2016 安装Docker 1 软件环境介绍2 下载更新2.1 windowsR 输入sconfig2.2 下载最新版的安装包&#xff0c;安装并重启2.3 下载并安装更新2.4 以管理员方式运行powershell2.5 将Tls修改成二级2.6 安装NuGet服务2.7 安装docker模块2.7 安装 docker包 32.8 查看…

Ajax的使用方法

1,什么是Ajax&#xff1f; Ajax&#xff08;异步Javascript和XML&#xff09;&#xff0c;是指一种创建交互式网页应用的网页开发技术。 2&#xff0c;Ajax的作用 Ajax可以使网页实现异步更新----即在不更新整个页面的情况下实现对某一部分进行更新。 简单来说Ajax就是用于连接…

顶级大厂Quora如何优化数据库性能?

Quora 的流量涉及大量阅读而非写入&#xff0c;一直致力于优化读和数据量而非写。 0 数据库负载的主要部分 读取数据量写入 1 优化读取 1.1 不同类型的读需要不同优化 ① 复杂查询&#xff0c;如连接、聚合等 在查询计数已成为问题的情况下&#xff0c;它们在另一个表中构…

HT for Web (Hightopo) 使用心得(5)- 动画的实现

其实&#xff0c;在 HT for Web 中&#xff0c;有多种手段可以用来实现动画。我们这里仍然用直升机为例&#xff0c;只是更换了场景。增加了巡游过程。 使用 HT 开发的一个简单网页直升机巡逻动画&#xff08;Hightopo 使用心得&#xff08;5&#xff09;&#xff09; 这里主…

纯js实现录屏并保存视频到本地的尝试

前言&#xff1a;先了解下&#xff1a;navigator.mediaDevices&#xff0c;mediaDevices 是 Navigator 只读属性&#xff0c;返回一个 MediaDevices 对象&#xff0c;该对象可提供对相机和麦克风等媒体输入设备的连接访问&#xff0c;也包括屏幕共享。 const media navigator…

【brew】Mac上安装vue3

先安装node。 这里我从其他博客找的方案&#xff0c;原始脚本下载太慢了。 cnpm的安装&#xff1a; 让npm更快一点。 npm install -g cnpm --registryhttps://registry.npm.taobao.org安装vue脚手架 2.0版本&#xff1a;sudo npm install -g vue-cli 3.0版本&#xff1a; sud…

2023.11.26使用opencv调节图片亮度

2023.11.26使用opencv调节图片亮度 测试一些opencv对图片的处理效果&#xff0c;方法比较简单&#xff0c;找出所有像素点&#xff0c;然后将RGB三色的亮度分别进行调节即可&#xff0c;同类可以进行像素级的处理。测试结果和项目代码如下&#xff1a; 使用OpenCV调节图拍亮…

Unity引擎:创造无限可能的游戏开发平台

Unity引擎&#xff1a;创造无限可能的游戏开发平台 一、Unity引擎概述1.1 什么是Unity引擎&#xff1f;1.2 Unity引擎的特点和优势 二、Unity开发环境和工具2.1 Unity编辑器2.2 支持的平台2.3 脚本语言2.4 图形和音频工具 三、Unity游戏开发流程四、示例应用场景五、结论&#…

java使用poi读写excel(处理上下标和科学计数法)

Background 要读写如下图所示的excel&#xff0c;符号和单位中包含上下标&#xff0c;在读写时需要特殊处理&#xff1b;取值列中是科学计数法&#xff0c;读写时需要特殊处理&#xff1b;excel中包含多个sheet&#xff0c;读的时候把所有sheet的数据读出来&#xff0c;写的时候…

分割掩模 VS 掩膜

掩膜 Mask分割掩模 Segmentation Mask总结示例 掩膜 Mask “掩膜” 是指一种用于 标识或遮蔽图像中特定区域 的 图像。 在图像处理中&#xff0c;掩膜通常是一个 二值图像&#xff0c;其中的 像素值为 0 或 1。binary Mask 叫做二元掩膜&#xff0c;如下图所示&#xff1a; 这…

Airtest进阶使用篇!提高脚本稳定性 + 批量运行脚本!

一、背景 今天彭于晏为大家分享Airtest进阶使用篇&#xff0c;主要包含两块的内容&#xff1a; 提高脚本稳定性批量运行脚本生成测试报告 二、提高脚本稳定性 1、添加全局配置: #全局设置 ST.FIND_TIMEOUT10 #设置隐式等待时长,默认识别图片时间是30秒&#xff0c;可改为…