修改截图保存文件夹

This commit is contained in:
2026-02-10 14:41:13 +08:00
parent 4e49793416
commit 6274c83dd5
12 changed files with 437 additions and 306 deletions

View File

@@ -4,6 +4,7 @@ import logging
import time
import re
import os
import threading
from datetime import datetime
from appium.webdriver.common.appiumby import AppiumBy
from selenium.common.exceptions import NoSuchElementException, TimeoutException
@@ -19,7 +20,7 @@ import globals.global_variable as global_variable # 导入全局变量模块
class ScreenshotPage:
def __init__(self, driver, wait, device_id=None):
self.driver = driver
self.wait = wait
self.wait = WebDriverWait(driver, 2)
self.device_id = device_id
self.logger = logging.getLogger(__name__)
self.all_items = set()
@@ -528,7 +529,7 @@ class ScreenshotPage:
self.logger.error(f"设备 {device_id} 检查WiFi状态时发生错误: {str(e)}")
return None
def take_screenshot(self, filename_prefix="screenshot"):
def take_screenshot(self, filename_prefix="screenshot", date_str=None, time_str=None):
"""
通过Appium驱动截取设备屏幕
@@ -539,20 +540,58 @@ class ScreenshotPage:
bool: 操作是否成功
"""
try:
# 创建测试结果目录
screenshots_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../test_results/screenshots')
if not os.path.exists(screenshots_dir):
os.makedirs(screenshots_dir)
self.logger.info(f"创建截图目录: {screenshots_dir}")
# 获取项目名称
project_name = global_variable.get_current_project_name() or "默认项目"
# 获取当前日期(如果没有提供)
if not date_str:
date_str = datetime.now().strftime("%Y%m%d")
# 获取当前时间(如果没有提供),并确保格式合法(不含冒号)
if not time_str:
time_str = datetime.now().strftime("%H%M%S")
else:
# 移除时间中的冒号,确保文件名合法
time_str = time_str.replace(":", "")
# 创建D盘下的截图目录结构D:\uploadInfo\picture\项目名\年月日
screenshots_dir = os.path.join("D:\\", "uploadInfo", "picture", project_name, date_str)
# 确保目录存在
try:
os.makedirs(screenshots_dir, exist_ok=True)
self.logger.info(f"截图目录: {screenshots_dir}")
except Exception as dir_error:
self.logger.error(f"创建截图目录失败: {str(dir_error)}")
return False
line_code = global_variable.get_upload_breakpoint_dict().get(filename_prefix)
if not line_code:
self.logger.error(f"未找到与断点名称 {filename_prefix} 对应的线路编码")
line_code = "unknown"
# 截图保存
screenshot_file = os.path.join(
screenshots_dir,
f"{filename_prefix}_{datetime.now().strftime('%Y%m%d')}.png"
f"{line_code}_{filename_prefix}_{time_str}.png"
)
self.driver.save_screenshot(screenshot_file)
self.logger.info(f"截图已保存: {screenshot_file}")
return True
# 尝试保存截图
try:
success = self.driver.save_screenshot(screenshot_file)
if success:
self.logger.info(f"截图已保存: {screenshot_file}")
# 验证文件是否真的存在
if os.path.exists(screenshot_file):
self.logger.info(f"截图文件验证存在: {screenshot_file}")
else:
self.logger.warning(f"截图文件保存成功但验证不存在: {screenshot_file}")
return True
else:
self.logger.error(f"Appium截图保存失败: {screenshot_file}")
return False
except Exception as save_error:
self.logger.error(f"保存截图时发生错误: {str(save_error)}")
return False
except Exception as e:
self.logger.error(f"截图时发生错误: {str(e)}")
@@ -591,117 +630,79 @@ class ScreenshotPage:
self.logger.error(f"写入截图状态文件时发生错误: {str(e)}")
return False
def wait_for_measurement_end(self, timeout=900):
def update_file_status(self, username, from_status, to_status):
"""
等待按钮变成"测量结束"最多15分钟包含驱动重新初始化机制
安全地更新 time.txt 中该用户的状态
例如: 将 'true' 改为 'running', 或将 'running' 改为 'done'
"""
TIME_FILE_PATH = r"D:\uploadInfo\time.txt"
if not os.path.exists(TIME_FILE_PATH):
return False
Args:
timeout: 超时时间默认900秒15分钟
success = False
file_lock = threading.Lock()
with file_lock:
try:
with open(TIME_FILE_PATH, 'r', encoding='utf-8') as f:
lines = f.readlines()
# new_lines = []
# for line in lines:
# clean_line = line.strip()
# # 匹配逻辑:包含用户名 且 以 from_status 结尾
# if f" {username} " in line and clean_line.endswith(from_status):
# line = line.replace(from_status, to_status)
# success = True
# new_lines.append(line)
new_lines = []
for line in lines:
# 使用正则确保精准匹配用户名和结尾状态
# 匹配规则:行内包含该用户名,且该行以 from_status 结尾
if re.search(rf'\b{username}\b', line) and line.strip().endswith(from_status):
# 只替换行尾的那个状态词
line = re.sub(rf'{from_status}$', to_status, line.rstrip()) + '\n'
success = True
new_lines.append(line)
with open(TIME_FILE_PATH, 'w', encoding='utf-8') as f:
f.writelines(new_lines)
if success:
print(f"📝 [文件更新] 用户 {username}: {from_status} -> {to_status}")
return success
except Exception as e:
print(f"❌ 更新文件状态失败 ({username}): {e}")
return False
def update_upload_info_status(self, status):
"""
更新D:/uploadInfo文件夹下的time.txt文件的状态
Returns:
bool: 是否成功等到测量结束按钮
参数:
status: 状态值,如"ok""again"
返回:
bool: 操作是否成功
"""
try:
# 更新WebDriverWait等待时间为900秒
self.wait = WebDriverWait(self.driver, 900)
self.logger.info(f"设备等待测量结束按钮出现,最多等待 {timeout}")
# time.txt文件路径
time_file_path = "D:\\uploadInfo\\time.txt"
start_time = time.time()
reinit_attempts = 0
max_reinit_attempts = 3 # 最大重新初始化次数
# 确保文件夹存在
os.makedirs(os.path.dirname(time_file_path), exist_ok=True)
while time.time() - start_time < timeout:
try:
# 使用XPath查找文本为"测量结束"的按钮
measurement_end_button = self.driver.find_element(
AppiumBy.XPATH,
"//android.widget.Button[@text='测量结束']"
)
if measurement_end_button.is_displayed() and measurement_end_button.is_enabled():
self.logger.info(f"设备检测到测量结束按钮")
return True
except NoSuchElementException:
# 按钮未找到,继续等待
pass
except Exception as e:
error_msg = str(e)
self.logger.warning(f"设备查找测量结束按钮时出现异常: {error_msg}")
# 检测是否是UiAutomator2服务崩溃
if 'UiAutomator2 server' in error_msg and 'instrumentation process is not running' in error_msg and reinit_attempts < max_reinit_attempts:
reinit_attempts += 1
self.logger.info(f"设备检测到UiAutomator2服务崩溃尝试第 {reinit_attempts} 次重新初始化驱动")
# 尝试重新初始化驱动
if self._reinit_driver():
self.logger.info(f"设备驱动重新初始化成功")
else:
self.logger.error(f"设备驱动重新初始化失败")
# 继续尝试,而不是立即失败
# 等待一段时间后再次检查
time.sleep(3)
# 每30秒输出一次等待状态
if int(time.time() - start_time) % 30 == 0:
elapsed = int(time.time() - start_time)
self.logger.info(f"设备 {self.device_id} 已等待 {elapsed} 秒,仍在等待测量结束...")
# 写入状态
with open(time_file_path, 'w', encoding='utf-8') as f:
f.write(status)
self.logger.error(f"设备 {self.device_id} 等待测量结束按钮超时")
return False
except Exception as e:
self.logger.error(f"设备 {self.device_id} 等待测量结束时发生错误: {str(e)}")
return False
def _reinit_driver(self):
"""
重新初始化Appium驱动
Returns:
bool: 是否成功重新初始化
"""
try:
# 首先尝试关闭现有的驱动
if hasattr(self, 'driver') and self.driver:
try:
self.driver.quit()
except:
self.logger.warning("关闭现有驱动时出现异常")
# 导入必要的模块
from appium import webdriver
from appium.options.android import UiAutomator2Options
# 重新创建驱动配置
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = self.device_id
options.app_package = "com.bjjw.cjgc"
options.app_activity = ".activity.LoginActivity"
options.automation_name = "UiAutomator2"
options.no_reset = True
options.auto_grant_permissions = True
options.new_command_timeout = 300
options.udid = self.device_id
# 重新连接驱动
self.logger.info(f"正在重新初始化设备 {self.device_id} 的驱动...")
self.driver = webdriver.Remote("http://localhost:4723", options=options)
# 重新初始化等待对象
from selenium.webdriver.support.ui import WebDriverWait
self.wait = WebDriverWait(self.driver, 1)
self.logger.info(f"设备 {self.device_id} 驱动重新初始化完成")
self.logger.info(f"已更新上传状态文件: {time_file_path} -> {status}")
return True
except Exception as e:
self.logger.error(f"设备 {self.device_id} 驱动重新初始化失败: {str(e)}")
self.logger.error(f"更新上传状态文件时发生错误: {str(e)}")
return False
def handle_confirmation_dialog(self, device_id, timeout=2):
"""
处理确认弹窗,点击""按钮
@@ -808,7 +809,7 @@ class ScreenshotPage:
if confirm_button and confirm_button.is_displayed() and confirm_button.is_enabled():
self.logger.info(f"设备 {device_id} 点击确认弹窗的''按钮")
confirm_button.click()
time.sleep(1)
time.sleep(0.5)
# 验证弹窗是否消失
try:
@@ -828,7 +829,7 @@ class ScreenshotPage:
except Exception as e:
self.logger.warning(f"设备 {device_id} 查找确认弹窗时出现异常: {str(e)}")
time.sleep(1)
time.sleep(0.5)
self.logger.error(f"设备 {device_id} 等待返回确认弹窗超时")
return False
@@ -1001,7 +1002,7 @@ class ScreenshotPage:
# 3. 验证是否成功返回到上一页面
time.sleep(1) # 等待页面跳转完成
time.sleep(0.5) # 等待页面跳转完成
# 可以添加页面验证逻辑,比如检查是否返回到预期的页面
# 这里可以根据实际应用添加特定的页面元素验证
@@ -1013,51 +1014,6 @@ class ScreenshotPage:
self.logger.error(f"设备 {device_id} 执行返回导航步骤时发生错误: {str(e)}")
return False
def scroll_to_bottom_and_screenshot(self, device_id):
"""
检测到测量结束后下滑列表到最底端点击最后一个spinner再下滑一次点击平差处理按钮后截图
Args:
device_id: 设备ID
Returns:
bool: 操作是否成功
"""
try:
self.logger.info(f"设备 {device_id} 开始执行测量结束后的操作流程")
time.sleep(5)
# 1. 下滑列表到最底端
if not self.scroll_list_to_bottom(device_id):
self.logger.error(f"设备 {device_id} 下滑列表到底部失败")
return False
# 2. 点击最后一个spinner
if not self.click_last_spinner_with_retry(device_id):
self.logger.error(f"设备 {device_id} 点击最后一个spinner失败")
return False
# 3. 再下滑一次
if not self.scroll_down_once(device_id):
self.logger.warning(f"设备 {device_id} 再次下滑失败,但继续执行")
# 4. 点击平差处理按钮
if not self.click_adjustment_button(device_id):
self.logger.error(f"设备 {device_id} 点击平差处理按钮失败")
return False
# 5. 在点击平差处理按钮后截图
time.sleep(2) # 等待平差处理按钮点击后的界面变化
if not self.take_screenshot("after_adjustment_button_click"):
self.logger.error(f"设备 {device_id} 截图失败")
return False
self.logger.info(f"设备 {device_id} 测量结束后操作流程完成")
return True
except Exception as e:
self.logger.error(f"设备 {device_id} 执行测量结束后操作时发生错误: {str(e)}")
return False
def scroll_list_to_bottom(self, device_id, max_swipes=60):
"""
@@ -1129,7 +1085,7 @@ class ScreenshotPage:
if self.click_last_spinner(device_id):
return True
self.logger.warning(f"设备 {device_id}{attempt + 1}次点击失败,准备重试")
time.sleep(1) # 重试前等待
time.sleep(0.5) # 重试前等待
except Exception as e:
self.logger.error(f"设备 {device_id}{attempt + 1}次尝试失败: {str(e)}")
@@ -1280,7 +1236,7 @@ class ScreenshotPage:
'percent': 0.5
})
time.sleep(1)
time.sleep(0.2)
self.logger.info(f"设备 {device_id} 额外下滑完成")
return True
@@ -1470,7 +1426,7 @@ class ScreenshotPage:
self.logger.error(f"设备 {device_id} 加载线路时间映射字典失败")
return False
time.sleep(5)
# time.sleep(5)
# 循环检查数据数量是否一致,直到获取到完整数据
retry_count = 0
@@ -1524,98 +1480,90 @@ class ScreenshotPage:
})
# 开始循环
all_success = True
for breakpoint_name in global_variable.get_upload_breakpoint_dict().keys():
self.logger.info(f"开始处理要平差截图的断点 {breakpoint_name}")
# 把断点名称给find_keyword
if not self.find_keyword(breakpoint_name):
self.logger.error(f"设备 {device_id} 未找到包含 {breakpoint_name} 的文件名")
return False
all_success = False
continue
if not self.handle_measurement_dialog():
self.logger.error(f"设备 {device_id} 处理测量弹窗失败")
return False
all_success = False
continue
if not self.check_apply_btn():
self.logger.error(f"设备 {device_id} 检查平差处理按钮失败")
return False
all_success = False
self.click_back_button(device_id)
continue
# 根据断点名称在get_upload_breakpoint_dict()中获取线路编码
line_code = global_variable.get_upload_breakpoint_dict().get(breakpoint_name)
if not line_code:
self.logger.error(f"设备 {device_id} 未找到断点 {breakpoint_name} 对应的线路编码")
return False
all_success = False
continue
# 根据线路编码查找对应的时间
date_str, time_str = self.get_line_end_time(line_code)
if not time_str or not date_str:
self.logger.error(f"设备 {device_id} 未找到线路 {line_code} 对应的时间")
return False
all_success = False
continue
# 修改时间
if not self.set_device_time(device_id, time_str, date_str):
self.logger.error(f"设备 {device_id} 设置设备时间失败")
return False
all_success = False
continue
# 滑动列表到底部
if not self.scroll_list_to_bottom(device_id):
self.logger.error(f"设备 {device_id} 下滑列表到底部失败")
return False
all_success = False
continue
# 2. 点击最后一个spinner
if not self.click_last_spinner_with_retry(device_id):
self.logger.error(f"设备 {device_id} 点击最后一个spinner失败")
return False
all_success = False
continue
# 3. 再下滑一次
if not self.scroll_down_once(device_id):
self.logger.warning(f"设备 {device_id} 再次下滑失败,但继续执行")
# # 4. 点击平差处理按钮
# if not self.click_adjustment_button(device_id):
# self.logger.error(f"设备 {device_id} 点击平差处理按钮失败")
# return False
# # 检查是否在测量页面在就重新执行选择断点滑动列表到底部点击最后一个spinner 再下滑一次,点击平差处理按钮平差
# if not self.handle_back_navigation(breakpoint_name, device_id):
# self.logger.error(f"{breakpoint_name}平差失败,未截图")
# return False
# # 检测并处理"是 保留成果"弹窗
# if not self.handle_adjustment_result_dialog():
# self.logger.error("处理平差结果弹窗失败")
# return False
# # 平差完成,将断点数据保存到上传列表中
# if not self.add_breakpoint_to_upload_list(breakpoint_name, line_code):
# self.logger.error(f"设备 {device_id} 保存断点 {breakpoint_name} 到上传列表失败")
# return False
# # 禁用WiFi
# if not self.disable_wifi(device_id):
# self.logger.error(f"设备 {device_id} 禁用WiFi失败")
# return False
# 平差处理完成后截图
time.sleep(3) # 等待平差处理按钮点击后的界面变化
time.sleep(0.2) # 等待平差处理按钮点击后的界面变化
logging.info("断点保存到上传列表成功,开始截图")
if not self.take_screenshot(breakpoint_name):
# png_time = date_str + " " + time_str
if not self.take_screenshot(breakpoint_name, date_str, time_str):
self.logger.error(f"设备 {device_id} 截图失败")
self.write_screenshot_status(breakpoint_name, success=False)
return False
self.write_screenshot_status(breakpoint_name, success=True)
all_success = False
else:
self.write_screenshot_status(breakpoint_name, success=True)
# 点击返回按钮并处理弹窗
if not self.execute_back_navigation_steps(device_id):
self.logger.error(f"设备 {device_id} 处理返回按钮确认失败")
return False
all_success = False
# 启用WiFi
# if not self.enable_wifi(device_id):
# self.logger.error(f"设备 {device_id} 启用WiFi失败")
# return False
self.logger.info(f"设备 {device_id} 截图页面操作执行完成")
# 根据截图结果更新time.txt文件状态
status = "ok" if all_success else "again"
# self.update_upload_info_status(status)
username = global_variable.get_username()
self.update_file_status(username, "running", status)
self.logger.info(f"{username} 截图完成状态为 {status}")
self.logger.info(f"设备 {device_id} 截图页面操作执行完成,状态: {status}")
return True
except Exception as e:
self.logger.error(f"设备 {device_id} 执行截图页面操作时出错: {str(e)}")