多设备启动,端口固定,时间接口未返回

This commit is contained in:
2026-02-04 10:58:46 +08:00
parent cc59e8b8da
commit 9ce1dbadc1
22 changed files with 1344 additions and 297 deletions

478
main_init.py Normal file
View File

@@ -0,0 +1,478 @@
# actions.py 主自动化脚本
import os
import logging
import time
import subprocess
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import globals.ids as ids
import globals.global_variable as global_variable # 导入全局变量模块
import permissions # 导入权限处理模块
import globals.apis as apis
from globals.driver_utils import init_appium_driver, ensure_appium_server_running, safe_quit_driver, is_app_launched, launch_app_manually
from page_objects.login_page import LoginPage
from page_objects.download_tabbar_page import DownloadTabbarPage
from page_objects.screenshot_page import ScreenshotPage
from page_objects.upload_config_page import UploadConfigPage
from page_objects.more_download_page import MoreDownloadPage
# 配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s: %(message)s",
handlers=[
logging.FileHandler("appium_automation.log"),
logging.StreamHandler()
]
)
class DeviceAutomation:
@staticmethod
def get_device_id() -> str:
# """
# 获取设备ID优先使用已连接设备否则使用全局配置
# """
# try:
# # 检查已连接设备
# result = subprocess.run(
# ["adb", "devices"],
# capture_output=True,
# text=True,
# timeout=10
# )
# # 解析设备列表
# for line in result.stdout.strip().split('\n')[1:]:
# if line.strip() and "device" in line and "offline" not in line:
# device_id = line.split('\t')[0]
# logging.info(f"使用已连接设备: {device_id}")
# global_variable.GLOBAL_DEVICE_ID = device_id
# return device_id
# except Exception as e:
# logging.warning(f"设备检测失败: {e}")
"""
获取设备ID优先使用无线连接设备否则尝试开启无线调试最后使用全局配置
"""
try:
# 检查已连接设备
result = subprocess.run(
["adb", "devices"],
capture_output=True,
text=True,
timeout=10
)
# 解析设备列表
wireless_device_id = None
usb_device_id = None
# 先查找无线连接的设备IP:端口格式)
for line in result.stdout.strip().split('\n')[1:]:
if line.strip() and "device" in line and "offline" not in line:
current_device = line.split('\t')[0]
# 检查是否为IP:端口格式的无线连接
if ":" in current_device and any(char.isdigit() for char in current_device):
wireless_device_id = current_device
logging.info(f"使用无线连接设备: {wireless_device_id}")
global_variable.GLOBAL_DEVICE_ID = wireless_device_id
return wireless_device_id
else:
# 记录第一个USB连接的设备
if not usb_device_id:
usb_device_id = current_device
# 如果没有找到无线连接的设备尝试使用USB设备开启无线调试
if not wireless_device_id and usb_device_id:
logging.info(f"未找到无线连接设备尝试使用USB设备 {usb_device_id} 开启无线调试")
# 尝试获取设备IP地址
try:
import re
import time
ip_result = subprocess.run(
["adb", "-s", usb_device_id, "shell", "ip", "-f", "inet", "addr", "show", "wlan0"],
capture_output=True,
text=True,
timeout=10
)
# 解析IP地址
ip_output = ip_result.stdout
if "inet " in ip_output:
# 提取IP地址
ip_match = re.search(r'inet\s+(\d+\.\d+\.\d+\.\d+)', ip_output)
if ip_match:
device_ip = ip_match.group(1)
logging.info(f"获取到设备IP地址: {device_ip}")
# 开启无线调试
tcpip_result = subprocess.run(
["adb", "-s", usb_device_id, "tcpip", "5555"],
capture_output=True,
text=True,
timeout=10
)
if "restarting in TCP mode port: 5555" in tcpip_result.stdout:
logging.info("无线调试已开启,端口: 5555")
# 等待几秒钟让设备准备好
time.sleep(3)
# 连接到无线设备
connect_result = subprocess.run(
["adb", "connect", f"{device_ip}:5555"],
capture_output=True,
text=True,
timeout=10
)
if "connected to" in connect_result.stdout:
logging.info(f"成功连接到无线设备: {device_ip}:5555")
global_variable.GLOBAL_DEVICE_ID = f"{device_ip}:5555"
return f"{device_ip}:5555"
else:
logging.warning(f"连接无线设备失败: {connect_result.stderr}")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
else:
logging.warning(f"开启无线调试失败: {tcpip_result.stderr}")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
else:
logging.warning("未找到设备IP地址")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
else:
logging.warning("无法获取设备IP地址可能设备未连接到WiFi")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
except Exception as e:
logging.warning(f"开启无线调试时出错: {str(e)}")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
except Exception as e:
logging.warning(f"设备检测失败: {e}")
# 使用全局配置
device_id = global_variable.GLOBAL_DEVICE_ID
logging.info(f"使用全局配置设备: {device_id}")
return device_id
def __init__(self, device_id=None):
# 如果没有提供设备ID则自动获取
if device_id is None:
self.device_id = self.get_device_id()
else:
self.device_id = device_id
# 初始化权限
if permissions.grant_appium_permissions(self.device_id):
logging.info(f"设备 {self.device_id} 权限授予成功")
else:
logging.warning(f"设备 {self.device_id} 权限授予失败")
# 确保Appium服务器正在运行
ensure_appium_server_running(4723)
# 初始化驱动
self.init_driver()
# 先拼接,后创建测试结果目录
self.results_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_results')
os.makedirs(self.results_dir, exist_ok=True)
def init_driver(self):
"""初始化Appium驱动"""
try:
# 使用全局函数初始化驱动
self.driver, self.wait = init_appium_driver(self.device_id)
# 初始化页面对象
logging.info(f"设备 {self.device_id} 开始初始化页面对象")
self.login_page = LoginPage(self.driver, self.wait)
self.download_tabbar_page = DownloadTabbarPage(self.driver, self.wait, self.device_id)
self.screenshot_page = ScreenshotPage(self.driver, self.wait, self.device_id)
self.upload_config_page = UploadConfigPage(self.driver, self.wait, self.device_id)
self.more_download_page = MoreDownloadPage(self.driver, self.wait,self.device_id)
logging.info(f"设备 {self.device_id} 所有页面对象初始化完成")
# 检查应用是否成功启动
if is_app_launched(self.driver):
logging.info(f"设备 {self.device_id} 沉降观测App已成功启动")
else:
logging.warning(f"设备 {self.device_id} 应用可能未正确启动")
# 手动启动应用
launch_app_manually(self.driver, self.device_id)
except Exception as e:
logging.error(f"设备 {self.device_id} 初始化驱动失败: {str(e)}")
raise
def is_app_launched(self):
"""检查应用是否已启动"""
try:
return is_app_launched(self.driver)
except Exception as e:
logging.error(f"设备 {self.device_id} 检查应用启动状态时出错: {str(e)}")
return False
def is_element_present(self, by, value):
"""检查元素是否存在"""
try:
self.driver.find_element(by, value)
return True
except NoSuchElementException:
return False
def handle_app_state(self):
"""根据当前应用状态处理相应的操作"""
try:
login_btn_exists = self.login_page.is_login_page()
if not login_btn_exists:
logging.error(f"设备 {self.device_id} 未知应用状态,无法确定当前页面,跳转到登录页面")
if self.navigate_to_login_page(self.driver, self.device_id):
logging.info(f"设备 {self.device_id} 成功跳转到登录页面")
return self.handle_app_state() # 递归调用处理登录后的状态
else:
logging.error(f"设备 {self.device_id} 跳转到登录页面失败")
return False
# 处理登录页面状态
logging.info(f"设备 {self.device_id} 检测到登录页面,执行登录操作")
max_retries = 1
login_success = False
for attempt in range(max_retries + 1):
if self.login_page.login():
login_success = True
break
else:
if attempt < max_retries:
logging.warning(f"设备 {self.device_id} 登录失败,准备重试 ({attempt + 1}/{max_retries})")
time.sleep(2) # 等待2秒后重试
else:
logging.error(f"设备 {self.device_id} 登录失败,已达到最大重试次数")
if not login_success:
return False
logging.info(f"设备 {self.device_id} 登录成功,继续执行更新操作")
time.sleep(1)
# 执行更新操作
if not self.download_tabbar_page.download_tabbar_page_manager():
logging.error(f"设备 {self.device_id} 更新操作执行失败")
return False
# 获取状态为3的线路。
apis.get_line_info_and_save_global(user_name=global_variable.GLOBAL_USERNAME);
# # 虚拟数据替代
# global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT = {'CDWZQ-2标-龙骨湾右线大桥-0-7号墩-平原': 'L156372', 'CDWZQ-2标-蓝家湾特大 桥-31-31-平原': 'L159206'}
# global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST = list(global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.keys())
# 点击测量导航栏按钮
measure_page_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/img_3_layout"))
)
measure_page_btn.click()
# 处理平差
self.screenshot_page.screenshot_page_manager(self.device_id)
# 检查是否有需要上传的断点
if not global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST:
logging.info(f"设备 {self.device_id} 断点列表为空,无需执行上传操作")
return False
# 点击上传导航栏按钮
upload_page_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/img_2_layout"))
)
upload_page_btn.click()
# 遍历断点列表,逐个执行上传操作
upload_success_count = 0
for breakpoint_name in global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST:
try:
logging.info(f"设备 {self.device_id} 开始处理断点 '{breakpoint_name}' 的上传")
# 安全地获取断点信息
line_num = global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.get(breakpoint_name)
if line_num is None:
logging.warning(f"设备 {self.device_id} 断点 '{breakpoint_name}' 在字典中未找到,跳过上传")
continue
if not line_num:
logging.warning(f"设备 {self.device_id} 断点 '{breakpoint_name}' 未获取到line_num跳过上传")
continue
# 执行上传配置管理,传入当前断点名称
if self.upload_config_page.upload_config_page_manager(self.results_dir, breakpoint_name, line_num):
logging.info(f"设备 {self.device_id} 断点 '{breakpoint_name}' 上传成功")
upload_success_count += 1
else:
logging.error(f"设备 {self.device_id} 断点 '{breakpoint_name}' 上传失败")
except Exception as e:
logging.error(f"设备 {self.device_id} 处理断点 '{breakpoint_name}' 时发生异常: {str(e)}")
logging.info(f"设备 {self.device_id} 上传配置管理执行完成,成功上传 {upload_success_count}/{len(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)} 个断点")
# 如果所有断点都上传成功返回True否则返回False
all_upload_success = upload_success_count == len(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)
if all_upload_success:
logging.info(f"设备 {self.device_id} 所有断点上传成功")
# 把上传成功的断点写入日志文件"上传成功的断点.txt"
with open(os.path.join(self.results_dir, "上传成功的断点.txt"), "w", encoding='utf-8') as f:
for bp in global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST:
f.write(f"{bp}\n")
else:
logging.warning(f"设备 {self.device_id} 部分断点上传失败")
# 把上传成功的断点写入日志文件"上传成功的断点.txt"
with open(os.path.join(self.results_dir, "上传成功的断点.txt"), "w", encoding='utf-8') as f:
for bp in global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST:
f.write(f"{bp}\n")
# 把上传失败的断点写入日志文件"上传失败的断点.txt"
with open(os.path.join(self.results_dir, "上传失败的断点.txt"), "w", encoding='utf-8') as f:
for bp in set(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)-set(global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST):
f.write(f"{bp}\n")
return all_upload_success
except Exception as e:
logging.error(f"设备 {self.device_id} 处理应用状态时出错: {str(e)}")
return False
def check_and_click_confirm_popup_appium(self):
"""
适用于Appium的弹窗检测函数
Returns:
bool: 是否成功处理弹窗
"""
try:
from appium.webdriver.common.appiumby import AppiumBy
# 使用self.driver而不是参数
if not hasattr(self, 'driver') or self.driver is None:
logging.warning("driver未初始化无法检测弹窗")
return False
# 检查弹窗消息
message_elements = self.driver.find_elements(AppiumBy.XPATH, '//android.widget.TextView[@text="是否退出测量界面?"]')
if message_elements:
logging.info("检测到退出测量界面弹窗")
# 点击"是"按钮
confirm_buttons = self.driver.find_elements(AppiumBy.XPATH, '//android.widget.Button[@text="" and @resource-id="android:id/button1"]')
if confirm_buttons:
confirm_buttons[0].click()
logging.info("已点击''按钮")
time.sleep(1)
return True
else:
logging.warning("未找到''按钮")
return False
else:
return False
except Exception as e:
logging.error(f"Appium检测弹窗时发生错误: {str(e)}")
return False
def navigate_to_login_page(self, driver, device_id):
"""
补充的跳转页面函数:当设备处于未知状态时,尝试跳转到登录页面
参数:
driver: 已初始化的Appium WebDriver对象
device_id: 设备ID用于日志记录
"""
try:
target_package = 'com.bjjw.cjgc'
target_activity = '.activity.LoginActivity'
# 使用ADB命令启动Activity
try:
logging.info(f"尝试使用ADB命令启动LoginActivity: {target_package}/{target_activity}")
adb_command = f"adb -s {device_id} shell am start -n {target_package}/{target_activity}"
result = subprocess.run(adb_command, shell=True, capture_output=True, text=True)
if result.returncode == 0:
logging.info(f"使用ADB命令启动LoginActivity成功")
time.sleep(2) # 等待Activity启动
return True
else:
logging.warning(f"ADB命令执行失败: {result.stderr}")
except Exception as adb_error:
logging.warning(f"执行ADB命令时出错: {adb_error}")
except Exception as e:
logging.error(f"跳转到登录页面过程中发生未预期错误: {e}")
# 所有尝试都失败
return False
def run_automation(self):
"""运行自动化流程"""
try:
success = self.handle_app_state()
# success = self.test_handle_app_state()
if success:
logging.info(f"设备 {self.device_id} 自动化流程执行成功")
else:
logging.error(f"设备 {self.device_id} 自动化流程执行失败")
return success
except Exception as e:
logging.error(f"设备 {self.device_id} 自动化执行过程中发生错误: {str(e)}")
return False
finally:
self.quit()
def quit(self):
"""关闭驱动"""
safe_quit_driver(getattr(self, 'driver', None), self.device_id)
# 主执行逻辑
if __name__ == "__main__":
# 单个设备配置 - 现在DeviceAutomation会自动获取设备ID
try:
automation = DeviceAutomation()
success = automation.run_automation()
if success:
logging.info(f"设备自动化流程执行成功")
else:
logging.error(f"设备自动化流程执行失败")
except Exception as e:
logging.error(f"设备执行出错: {str(e)}")