Files
cjgc_upload/globals/driver_utils.py
2026-02-02 11:47:53 +08:00

764 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
import time
import subprocess
import traceback
import socket
import os
import requests
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.appium_service import AppiumService
from appium.options.android import UiAutomator2Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException, InvalidSessionIdException, WebDriverException
import globals.global_variable as global_variable
def init_appium_driver(device_id, app_package="com.bjjw.cjgc", app_activity=".activity.LoginActivity"):
"""
初始化Appium驱动的全局函数
参数:
device_id: 设备ID
app_package: 应用包名,默认为"com.bjjw.cjgc"
app_activity: 应用启动Activity默认为".activity.LoginActivity"
返回:
(driver, wait): (WebDriver实例, WebDriverWait实例),如果初始化失败则抛出异常
"""
logging.info(f"设备 {device_id} 开始初始化Appium驱动")
# 创建并配置Appium选项
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = device_id
options.app_package = app_package
options.app_activity = app_activity
options.automation_name = "UiAutomator2"
options.no_reset = True
options.auto_grant_permissions = True
options.new_command_timeout = 28800
options.udid = device_id
# 增加uiautomator2服务器启动超时时间
options.set_capability('uiautomator2ServerLaunchTimeout', 60000) # 60秒
# 增加连接超时设置
options.set_capability('connection_timeout', 120000) # 120秒
try:
# 连接Appium服务器
driver_url = "http://127.0.0.1:4723/wd/hub"
logging.info(f"设备 {device_id} 正在连接Appium服务器: {driver_url}")
driver = webdriver.Remote(driver_url, options=options)
logging.info(f"设备 {device_id} Appium服务器连接成功")
# 初始化等待对象
wait = WebDriverWait(driver, 20)
logging.info(f"设备 {device_id} WebDriverWait初始化成功")
# 等待应用稳定
time.sleep(2)
logging.info(f"设备 {device_id} Appium驱动初始化完成")
return driver, wait
except Exception as e:
logging.error(f"设备 {device_id} 初始化驱动失败: {str(e)}")
logging.error(f"错误类型: {type(e).__name__}")
logging.error(f"错误堆栈: {traceback.format_exc()}")
# 如果驱动已创建,尝试关闭
if 'driver' in locals() and driver:
try:
driver.quit()
except:
pass
raise
def check_session_valid(driver, device_id=None):
"""
检查当前会话是否有效
参数:
driver: WebDriver实例
device_id: 设备ID可选
返回:
bool: 会话有效返回True否则返回False
"""
if device_id is None:
device_id = global_variable.GLOBAL_DEVICE_ID
device_str = f"设备 {device_id} " if device_id else ""
if not driver:
logging.debug(f"{device_str}驱动实例为空")
return False
try:
# 首先检查driver是否有session_id属性
if not hasattr(driver, 'session_id') or not driver.session_id:
logging.debug(f"{device_str}驱动缺少有效的session_id")
return False
# 尝试获取当前上下文
current_context = driver.current_context
logging.debug(f"{device_str}会话检查通过,当前上下文: {current_context}")
return True
except InvalidSessionIdException:
logging.debug(f"{device_str}会话已失效")
return False
except WebDriverException as e:
error_msg = str(e).lower()
# 明确的会话失效错误
if any(phrase in error_msg for phrase in [
"session is either terminated or not started",
"could not proxy command to the remote server",
"socket hang up",
"connection refused",
"max retries exceeded"
]):
logging.debug(f"{device_str}会话连接错误: {error_msg[:100]}")
return False
else:
logging.debug(f"{device_str}WebDriver异常但可能不是会话失效: {error_msg[:100]}")
return True
except (ConnectionError, ConnectionRefusedError, ConnectionResetError) as e:
logging.debug(f"{device_str}网络连接错误: {str(e)}")
return False
except Exception as e:
error_msg = str(e)
# 检查是否是连接相关错误
if any(phrase in error_msg.lower() for phrase in [
"10054", "10061", "connection", "connect", "refused", "urllib3"
]):
logging.debug(f"{device_str}连接相关异常: {error_msg[:100]}")
return False
else:
logging.debug(f"{device_str}检查会话时出现其他异常: {error_msg[:100]}")
return True # 对于真正的未知异常保守返回True
def reconnect_driver(device_id, old_driver=None, app_package="com.bjjw.cjgc", app_activity=".activity.LoginActivity"):
"""
重新连接Appium驱动不重新启动应用
参数:
device_id: 设备ID
old_driver: 旧的WebDriver实例可选
app_package: 应用包名
app_activity: 应用启动Activity
返回:
(driver, wait): 新的WebDriver和WebDriverWait实例
"""
# 使用传入的device_id或从全局变量获取
if not device_id:
device_id = global_variable.GLOBAL_DEVICE_ID
# 修复device_id参数类型问题并使用全局设备ID作为备用
actual_device_id = device_id
# 检查device_id是否为有效的字符串格式
if not actual_device_id or (isinstance(actual_device_id, str) and ("session=" in actual_device_id or len(actual_device_id.strip()) == 0)):
# 尝试从old_driver获取设备ID
if old_driver and hasattr(old_driver, 'capabilities'):
capability_device_id = old_driver.capabilities.get('udid')
if capability_device_id:
actual_device_id = capability_device_id
logging.warning(f"检测到device_id参数无效已从old_driver中提取设备ID: {actual_device_id}")
# 如果仍然没有有效的设备ID使用全局变量
if not actual_device_id or (isinstance(actual_device_id, str) and ("session=" in actual_device_id or len(actual_device_id.strip()) == 0)):
actual_device_id = global_variable.GLOBAL_DEVICE_ID
logging.warning(f"无法获取有效设备ID使用全局变量GLOBAL_DEVICE_ID: {actual_device_id}")
device_id = actual_device_id # 使用修正后的设备ID
logging.info(f"设备 {device_id} 开始重新连接驱动(不重启应用)")
# # 首先安全关闭旧驱动
# if old_driver:
# safe_quit_driver(old_driver, device_id)
max_reconnect_attempts = 3
reconnect_delay = 5 # 秒
for attempt in range(max_reconnect_attempts):
try:
logging.info(f"设备 {device_id}{attempt + 1}次尝试重新连接")
# 确保Appium服务器运行
if not ensure_appium_server_running():
logging.warning(f"设备 {device_id} Appium服务器未运行尝试启动")
time.sleep(reconnect_delay)
continue
# 创建并配置Appium选项 - 重点:设置 autoLaunch=False 不自动启动应用
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = device_id
options.app_package = app_package
options.app_activity = app_activity
options.automation_name = "UiAutomator2"
options.no_reset = True
options.auto_grant_permissions = True
options.new_command_timeout = 3600
options.udid = device_id
# 关键设置:不自动启动应用
options.set_capability('autoLaunch', False)
options.set_capability('skipUnlock', True)
options.set_capability('skipServerInstallation', True)
options.set_capability('skipDeviceInitialization', True)
# 增加uiautomator2服务器启动超时时间
options.set_capability('uiautomator2ServerLaunchTimeout', 60000) # 60秒
# 增加连接超时设置
options.set_capability('connection_timeout', 120000) # 120秒
# 连接Appium服务器
logging.info(f"设备 {device_id} 正在连接Appium服务器不启动应用")
driver = webdriver.Remote("http://localhost:4723", options=options)
logging.info(f"设备 {device_id} Appium服务器连接成功")
# 初始化等待对象
wait = WebDriverWait(driver, 20)
logging.info(f"设备 {device_id} WebDriverWait初始化成功")
# 不启动应用,直接附加到当前运行的应用
try:
# 获取当前运行的应用
current_package = driver.current_package
logging.info(f"设备 {device_id} 当前运行的应用: {current_package}")
# 如果当前运行的不是目标应用,尝试切换到目标应用
if current_package != app_package:
logging.info(f"设备 {device_id} 当前应用不是目标应用,尝试启动目标应用")
launch_app_manually(driver, app_package, app_activity)
else:
logging.info(f"设备 {device_id} 已成功连接到运行中的目标应用")
except Exception as attach_error:
logging.warning(f"设备 {device_id} 获取当前应用信息失败: {str(attach_error)}")
# 即使获取当前应用失败,也继续使用连接
# 验证新会话是否有效
if check_session_valid(driver, device_id):
logging.info(f"设备 {device_id} 重新连接成功")
return driver, wait
else:
logging.warning(f"设备 {device_id} 新创建的会话无效,将重试")
safe_quit_driver(driver, device_id)
except Exception as e:
logging.error(f"设备 {device_id}{attempt + 1}次重新连接失败: {str(e)}")
if attempt < max_reconnect_attempts - 1:
wait_time = reconnect_delay * (attempt + 1)
logging.info(f"设备 {device_id} 将在{wait_time}秒后重试重新连接")
time.sleep(wait_time)
else:
logging.error(f"设备 {device_id} 所有重新连接尝试均失败")
# 所有尝试都失败后才关闭旧驱动
if old_driver:
safe_quit_driver(old_driver, device_id)
raise
# 所有尝试都失败
raise Exception(f"设备 {device_id} 重新连接失败,已尝试{max_reconnect_attempts}")
# def ensure_appium_server_running(port=4723):
# """使用完整的环境变量启动Appium解决路径缺失问题"""
# try:
# # 获取当前用户的环境变量
# env = os.environ.copy()
# # ========== 仅修改这部分Windows下Node.js/Appium常见路径 ==========
# additional_paths = [
# # Windows默认Node.js安装路径64位
# os.path.join("C:\\", "Program Files\\nodejs"),
# # Windows32位Node.js路径
# os.path.join("C:\\", "Program Files (x86)\\nodejs"),
# # npm全局安装路径Windows核心appium一般装在这里
# os.path.expanduser("~\\AppData\\Roaming\\npm"),
# # 系统默认路径(防止基础命令缺失)
# os.path.join("C:\\", "Windows\\System32"),
# os.path.join("C:\\", "Windows\\System"),
# # 自定义Node.js/npm路径可选根据你的实际安装路径加
# # "D:\\Program Files\\nodejs", # 若你装在D盘解开注释并修改
# ]
# # ========== Windows路径修改结束 ==========
# # 更新PATH环境变量跨系统通用os.pathsep自动适配Windows的;和macOS的:
# current_path = env.get('PATH', '')
# new_path = current_path + os.pathsep + os.pathsep.join(additional_paths)
# env['PATH'] = new_path
# # 构建启动命令(和原函数一致,无修改)
# appium_cmd = f"appium -p {port} --log-level error"
# # 使用完整环境启动跨系统通用Windows下正常执行
# process = subprocess.Popen(
# appium_cmd,
# shell=True, # Windows下字符串命令必须开启和macOS一致
# env=env, # 传入补全后的Windows环境变量核心
# stdout=subprocess.PIPE, # 捕获输出,控制台不刷屏
# stderr=subprocess.PIPE,
# text=True # 输出为字符串,无需手动解码(跨系统通用)
# )
# logging.info(f"Appium启动进程已创建PID: {process.pid}")
# # 等待并校验服务启动成功需确保wait_for_appium_start已定义timeout有值
# return wait_for_appium_start(port)
# except Exception as e:
# logging.error(f"Windows下启动Appium时出错: {str(e)}")
# return False
def is_port_in_use(port):
"""检查端口是否被占用"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
return s.connect_ex(('127.0.0.1', port)) == 0
def kill_system_process(process_name):
"""杀掉系统进程"""
try:
# Windows
subprocess.run(f"taskkill /F /IM {process_name}", shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
except Exception:
pass
def start_appium_server(port=4723):
"""启动 Appium 服务,强制指定路径兼容性"""
# 1. 先尝试清理可能占用的 node 进程
if is_port_in_use(port):
logging.warning(f"端口 {port} 被占用,尝试清理 node.exe...")
kill_system_process("node.exe")
time.sleep(2)
# 2. 构造启动命令
# 注意:这里增加了 --base-path /wd/hub 解决 404 问题
# --allow-cors 允许跨域,有时候能解决连接问题
appium_cmd = f"appium -p {port} --base-path /wd/hub --allow-cors"
logging.info(f"正在启动 Appium: {appium_cmd}")
try:
# 使用 shell=True 在 Windows 上更稳定
# creationflags=subprocess.CREATE_NEW_CONSOLE 可以让它在后台运行不弹出窗口
subprocess.Popen(appium_cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
logging.info("Appium 启动命令已发送,等待服务就绪...")
except Exception as e:
logging.error(f"启动 Appium 进程失败: {e}")
def check_server_status(port):
"""检测服务器状态,兼容 Appium 1.x 和 2.x 路径"""
base_url = f"http://127.0.0.1:{port}"
check_paths = ["/wd/hub/status", "/status"] # 优先检查 /wd/hub
for path in check_paths:
try:
url = f"{base_url}{path}"
response = requests.get(url, timeout=1)
if response.status_code == 200:
return True
except:
pass
return False
def ensure_appium_server_running(port=4723):
"""确保 Appium 服务器正在运行,如果没运行则启动它"""
# 1. 第一次快速检测
if check_server_status(port):
logging.info(f"Appium 服务已在端口 {port} 运行")
return True
# 2. 如果没运行,启动它
logging.warning(f"Appium 未在端口 {port} 运行,准备启动...")
start_appium_server(port)
# 3. 循环等待启动成功(最多等待 20 秒)
max_retries = 20
for i in range(max_retries):
if check_server_status(port):
logging.info("Appium 服务启动成功并已就绪!")
return True
time.sleep(1)
if i % 5 == 0:
logging.info(f"等待 Appium 启动中... ({i}/{max_retries})")
logging.error("Appium 服务启动超时!请检查 appium 命令是否在命令行可直接运行。")
return False
# ... (保留 init_appium_driver, safe_quit_driver 等其他函数) ...
def wait_for_appium_start(port, timeout=10):
"""
检测指定端口的Appium服务是否真正启动并可用
:param port: Appium服务端口
:param timeout: 最大等待时间(秒)
:return: 服务就绪返回True超时/失败返回False
"""
# Appium官方状态查询接口
check_url = f"http://localhost:{port}/wd/hub/status"
# 检测开始时间
start_check_time = time.time()
logging.info(f"开始检测Appium服务是否就绪端口{port},最大等待{timeout}")
while time.time() - start_check_time < timeout:
try:
# 发送HTTP请求超时1秒避免单次检测卡太久
response = requests.get(
url=check_url,
timeout=1,
verify=False # 本地接口禁用SSL验证
)
# 接口返回200HTTP成功状态码且JSON中status=0Appium服务正常
if response.status_code == 200 and response.json().get("status") == 0:
logging.info(f"Appium服务检测成功端口{port}已就绪")
return True
except Exception as e:
# 捕获所有异常连接拒绝、超时、JSON解析失败等说明服务未就绪
logging.debug(f"本次检测Appium服务未就绪{str(e)}") # 调试日志,不刷屏
# 检测失败休眠1秒后重试
time.sleep(1)
# 循环结束→超时
logging.error(f"检测超时!{timeout}秒内Appium服务端口{port}仍未就绪")
return False
def safe_quit_driver(driver, device_id=None):
"""
安全关闭驱动的全局函数
参数:
driver: WebDriver实例
device_id: 设备ID可选
"""
if device_id is None:
device_id = global_variable.GLOBAL_DEVICE_ID
device_str = f"设备 {device_id} " if device_id else ""
logging.info(f"{device_str}开始关闭驱动")
if not driver:
logging.info(f"{device_str}没有可关闭的驱动实例")
return
# 检查driver是否为WebDriver实例或是否有quit方法
if not hasattr(driver, 'quit'):
logging.warning(f"{device_str}驱动对象类型无效不具有quit方法: {type(driver).__name__}")
return
max_quit_attempts = 3
for attempt in range(max_quit_attempts):
try:
logging.info(f"{device_str}尝试关闭驱动 (尝试 {attempt + 1}/{max_quit_attempts})")
driver.quit()
logging.info(f"{device_str}驱动已成功关闭")
return
except Exception as e:
logging.error(f"{device_str}关闭驱动时出错 (尝试 {attempt + 1}/{max_quit_attempts}): {str(e)}")
if attempt < max_quit_attempts - 1:
# 等待一段时间后重试
wait_time = 2
logging.info(f"{device_str}将在 {wait_time} 秒后重试")
time.sleep(wait_time)
else:
logging.critical(f"{device_str}尝试多次关闭驱动失败,可能导致资源泄漏")
def is_app_launched(driver, package_name="com.bjjw.cjgc"):
"""
检查应用是否已启动
参数:
driver: WebDriver实例
package_name: 应用包名,默认为"com.bjjw.cjgc"
返回:
bool: 如果应用已启动则返回True否则返回False
"""
try:
# 通过检查当前活动的包名来确认应用是否已启动
current_package = driver.current_package
return current_package == package_name
except Exception as e:
logging.error(f"检查应用启动状态时出错: {str(e)}")
return False
def launch_app_manually(driver, device_id, package_name="com.bjjw.cjgc", activity=".activity.LoginActivity"):
"""
手动启动应用
参数:
driver: WebDriver实例
package_name: 应用包名,默认为"com.bjjw.cjgc"
activity: 应用启动Activity默认为".activity.LoginActivity"
"""
try:
if not device_id:
device_id = global_variable.GLOBAL_DEVICE_ID
# 尝试从driver获取设备ID
if driver and hasattr(driver, 'capabilities'):
device_id = driver.capabilities.get('udid')
device_str = f"设备 {device_id} " if device_id else ""
else:
device_str = ""
logging.info(f"{device_str}尝试手动启动应用: {package_name}/{activity}")
# 首先使用ADB命令退出应用
if device_id:
try:
# 使用ADB命令强制停止应用
stop_cmd = [
"adb", "-s", device_id,
"shell", "am", "force-stop",
package_name
]
stop_result = subprocess.run(stop_cmd, capture_output=True, text=True, timeout=15)
if stop_result.returncode == 0:
logging.info(f"{device_str}已使用ADB命令成功退出应用")
else:
logging.warning(f"{device_str}ADB退出应用失败: {stop_result.stderr}")
except Exception as stop_error:
logging.warning(f"{device_str}退出应用时出错: {str(stop_error)}")
# 首先尝试使用driver的execute_script方法启动应用
try:
if driver:
driver.execute_script("mobile: startActivity", {
"intent": f"{package_name}/{activity}"
})
logging.info(f"{device_str}已使用Appium startActivity命令启动应用")
except Exception as inner_e:
logging.warning(f"{device_str}使用Appium startActivity命令失败: {str(inner_e)}尝试使用ADB命令")
# 如果device_id可用使用ADB命令启动应用
if device_id:
cmd = [
"adb", "-s", device_id,
"shell", "am", "start",
"-n", f"{package_name}/{activity}"
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
if result.returncode == 0:
logging.info(f"{device_str}已使用ADB命令成功启动应用")
else:
logging.error(f"{device_str}ADB启动应用失败: {result.stderr}")
else:
logging.warning("无法获取设备ID无法使用ADB命令启动应用")
# 设置屏幕永不休眠
if device_id:
try:
# 使用ADB命令设置屏幕永不休眠
screen_timeout_cmd = [
"adb", "-s", device_id,
"shell", "settings", "put", "system", "screen_off_timeout", "0"
]
timeout_result = subprocess.run(screen_timeout_cmd, capture_output=True, text=True, timeout=15)
if timeout_result.returncode == 0:
logging.info(f"{device_str}已成功设置屏幕永不休眠")
else:
logging.warning(f"{device_str}设置屏幕永不休眠失败: {timeout_result.stderr}")
except Exception as timeout_error:
logging.warning(f"{device_str}设置屏幕永不休眠时出错: {str(timeout_error)}")
# 等待应用启动
time.sleep(5)
except Exception as e:
logging.error(f"手动启动应用时出错: {str(e)}")
logging.error(f"错误堆栈: {traceback.format_exc()}")
def go_main_click_tabber_button(driver, device_id, tabber_button_text, max_retries=3):
"""
跳转到主页面并点击对应的导航菜单按钮(带重试机制)
参数:
driver: WebDriver实例
device_id: 设备ID
tabber_button_text: 导航菜单按钮的文本
max_retries: 最大重试次数
返回:
bool: 成功返回True失败返回False
"""
retry_count = 0
while retry_count < max_retries:
try:
logging.info(f"设备 {device_id}{retry_count + 1} 次尝试执行导航操作")
if not check_session_valid(driver, device_id):
logging.warning(f"设备 {device_id} 会话无效,尝试重新连接驱动...")
try:
# 重新连接获取新的driver
new_driver, _ = reconnect_driver(device_id, driver)
driver = new_driver # 更新driver引用
logging.info(f"设备 {device_id} 驱动重连成功")
except Exception as e:
logging.error(f"设备 {device_id} 驱动重连失败: {str(e)}")
retry_count += 1
if retry_count < max_retries:
time.sleep(2)
continue
else:
return False
# 检查当前是否已经在主页面
current_activity = driver.current_activity
logging.info(f"设备 {device_id} 当前Activity: {current_activity}")
if ".activity.MainActivity" in current_activity:
logging.info(f"设备 {device_id} 已在主页面")
else:
logging.info(f"设备 {device_id} 当前不在主页面,循环点击返回按钮,直到在主页面")
max_back_presses = 10 # 最大返回键次数
back_press_count = 0
while ".activity.MainActivity" not in current_activity and back_press_count < max_back_presses:
try:
if not check_session_valid(driver, device_id):
logging.warning(f"设备 {device_id} 会话无效,尝试重新连接驱动...")
if not reconnect_driver(device_id, driver):
logging.error(f"设备 {device_id} 驱动重连失败")
# 点击返回按钮
driver.back()
back_press_count += 1
time.sleep(1)
# 更新当前Activity
current_activity = driver.current_activity
logging.info(f"设备 {device_id} 点击返回按钮 {back_press_count} 次后当前Activity: {current_activity}")
except Exception as inner_e:
logging.warning(f"设备 {device_id} 点击返回按钮时出错: {str(inner_e)}")
break
# 检查是否成功回到主页面
if ".activity.MainActivity" not in current_activity:
logging.warning(f"设备 {device_id} 无法回到主页面当前Activity: {current_activity}")
# 不立即返回,继续重试逻辑
retry_count += 1
if retry_count < max_retries:
logging.info(f"设备 {device_id} 等待2秒后重试...")
time.sleep(2)
continue
else:
logging.error(f"设备 {device_id} 达到最大重试次数,无法回到主页面")
return False
# 现在已经在主页面,点击指定的导航菜单按钮
# logging.info(f"设备 {device_id} 已在主页面,尝试点击导航菜单按钮: {tabber_button_text}")
try:
tabber_button = driver.find_element(AppiumBy.ID, tabber_button_text)
# 点击按钮
tabber_button.click()
logging.info(f"设备 {device_id} 已成功点击导航菜单按钮: {tabber_button_text}")
# 等待页面加载
time.sleep(2)
# 验证操作是否成功
# 可以添加一些验证逻辑,比如检查是否跳转到目标页面
new_activity = driver.current_activity
logging.info(f"设备 {device_id} 点击后当前Activity: {new_activity}")
return True
except TimeoutException:
logging.error(f"设备 {device_id} 等待导航菜单按钮 '{tabber_button_text}' 超时")
except Exception as e:
logging.error(f"设备 {device_id} 点击导航菜单按钮 '{tabber_button_text}' 时出错: {str(e)}")
# 检查会话有效性并尝试重连
if not check_session_valid(driver, device_id):
logging.warning(f"设备 {device_id} 会话无效,尝试重新连接驱动...")
if reconnect_driver(device_id, driver):
logging.info(f"设备 {device_id} 驱动重连成功,继续重试")
# 重连成功后继续循环
retry_count += 1
if retry_count < max_retries:
time.sleep(2)
continue
else:
logging.error(f"设备 {device_id} 驱动重连失败")
return False
else:
# 会话有效但点击失败,可能是页面元素问题
logging.warning(f"设备 {device_id} 会话有效但点击失败,可能是页面加载问题")
# 如果点击按钮失败,增加重试计数
retry_count += 1
if retry_count < max_retries:
logging.info(f"设备 {device_id} 点击按钮失败等待2秒后第 {retry_count + 1} 次重试...")
time.sleep(2)
else:
logging.error(f"设备 {device_id} 达到最大重试次数 {max_retries},导航失败")
return False
except Exception as e:
logging.error(f"设备 {device_id}{retry_count + 1} 次尝试时出错: {str(e)}")
retry_count += 1
if retry_count < max_retries:
logging.info(f"设备 {device_id} 等待2秒后重试...")
time.sleep(2)
else:
logging.error(f"设备 {device_id} 达到最大重试次数 {max_retries},导航失败")
return False
return False
def check_connection_error(exception):
"""检查是否为连接拒绝错误"""
error_str = str(exception)
connection_errors = [
'远程主机强迫关闭了一个现有的连接',
'由于目标计算机积极拒绝,无法连接',
'ConnectionResetError',
'NewConnectionError',
'10054',
'10061'
]
return any(error in error_str for error in connection_errors)
def restart_appium_server(port=4723):
"""重启Appium服务器"""
try:
# 杀死可能存在的Appium进程
subprocess.run(['taskkill', '/f', '/im', 'node.exe'],
capture_output=True, shell=True)
time.sleep(2)
# 启动Appium服务器
# appium_command = f'appium -p {port}'
appium_command = f"appium -p {port} --base-path /wd/hub"
subprocess.Popen(appium_command, shell=True)
# 等待Appium启动
time.sleep(10)
return True
except Exception as e:
print(f"重启Appium服务器失败: {str(e)}")
return False
def is_appium_running(port=4723):
"""检查Appium服务器是否在运行"""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
result = s.connect_ex(('127.0.0.1', port))
return result == 0
except:
return False