first commit

This commit is contained in:
2026-03-12 17:03:56 +08:00
commit aa4d4c7d7c
48 changed files with 10958 additions and 0 deletions

Binary file not shown.

46
page_objects/call_xie.py Normal file
View File

@@ -0,0 +1,46 @@
import socket
def send_tcp_command(command: str, host: str = '127.0.0.1', port: int = 8888, encoding: str = 'utf-8') -> bool:
"""
向指定TCP端口发送指令
参数:
command: 要发送的指令字符串
host: 目标主机地址默认127.0.0.1
port: 目标端口默认8888
encoding: 字符串编码格式默认utf-8
返回:
发送成功返回True失败返回False
"""
# 创建TCP socket并自动关闭with语句确保资源释放
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
try:
# 连接服务器超时时间5秒避免无限阻塞
sock.settimeout(5.0)
sock.connect((host, port))
# 发送指令(转换为字节流)
sock.sendall(command.encode(encoding))
print(f"指令 '{command}' 发送成功")
return True
except ConnectionRefusedError:
print(f"连接失败:{host}:{port} 未监听或不可达")
except socket.timeout:
print(f"连接超时超过5秒未连接到 {host}:{port}")
except UnicodeEncodeError:
print(f"编码失败:指令包含{encoding}无法编码的字符")
except Exception as e:
print(f"发送失败:{str(e)}")
return False
# 使用示例
if __name__ == "__main__":
# 发送StartConnect指令
send_tcp_command("StartConnect")
# 也可以发送其他指令,例如:
# send_tcp_command("StopConnect")

View File

@@ -0,0 +1,405 @@
# 更新基站页面操作
# page_objects/download_tabbar_page.py
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, StaleElementReferenceException
import logging
import time
from datetime import datetime
import globals.ids as ids # 导入元素ID
import globals.global_variable as global_variable # 导入全局变量
from globals.driver_utils import check_session_valid, reconnect_driver
class DownloadTabbarPage:
def __init__(self, driver, wait, device_id):
self.driver = driver
self.wait = wait
self.device_id = device_id
self.logger = logging.getLogger(__name__)
# 添加默认的目标日期值
self.target_year = 2022
self.target_month = 9
self.target_day = 22
def is_download_tabbar_visible(self):
"""检查下载标签栏是否可见"""
try:
return self.driver.find_element(AppiumBy.ID, ids.DOWNLOAD_TABBAR_ID).is_displayed()
except NoSuchElementException:
self.logger.warning("下载标签栏元素未找到")
return False
except Exception as e:
self.logger.error(f"检查下载标签栏可见性时发生意外错误: {str(e)}")
return False
def click_download_tabbar(self):
"""点击下载标签栏"""
try:
download_tab = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.DOWNLOAD_TABBAR_ID))
)
download_tab.click()
self.logger.info("已点击下载标签栏")
# 使用显式等待替代固定等待
self.wait.until(
lambda driver: self.is_download_tabbar_visible()
)
return True
except TimeoutException:
self.logger.error("等待下载标签栏可点击超时")
return False
except Exception as e:
self.logger.error(f"点击下载标签栏时出错: {str(e)}")
return False
def update_work_base(self):
"""更新工作基点"""
try:
# 点击更新工作基点
update_work_base = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.UPDATE_WORK_BASE))
)
update_work_base.click()
self.logger.info("已点击更新工作基点")
# 等待更新完成 - 可以添加更具体的等待条件
# 例如等待某个进度条消失或成功提示出现
time.sleep(2) # 暂时保留,但建议替换为显式等待
return True
except TimeoutException:
self.logger.error("等待更新工作基点按钮可点击超时")
return False
except Exception as e:
self.logger.error(f"更新工作基点时出错: {str(e)}")
return False
def _get_current_date(self):
"""获取当前开始日期控件的日期值,支持多种格式解析"""
try:
date_element = self.wait.until(
EC.visibility_of_element_located((AppiumBy.ID, ids.DATE_START))
)
date_text = date_element.text.strip()
self.logger.info(f"获取到当前开始日期: {date_text}")
# 尝试多种日期格式解析
date_formats = [
"%Y-%m-%d", # 匹配 '2025-08-12' 格式
"%Y年%m月%d", # 匹配 '2025年08月12日' 格式
"%Y/%m/%d" # 可选:添加其他可能的格式
]
for fmt in date_formats:
try:
return datetime.strptime(date_text, fmt)
except ValueError:
continue # 尝试下一种格式
# 如果所有格式都匹配失败
self.logger.error(f"日期格式解析错误: 无法识别的格式,日期文本: {date_text}")
return None
except TimeoutException:
self.logger.error("获取当前日期超时")
return None
except Exception as e:
self.logger.error(f"获取当前日期失败: {str(e)}")
return None
def update_level_line(self):
"""更新水准线路修改为设置2022年9月22日"""
try:
# 点击更新水准线路
update_level_line = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.UPDATE_LEVEL_LINE))
)
update_level_line.click()
self.logger.info("已点击更新水准线路")
# 获取原始开始日期
original_date = self._get_current_date()
if not original_date:
self.logger.error("无法获取原始开始日期,更新水准线路失败")
return False
# 点击开始日期
date_start = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.DATE_START))
)
date_start.click()
self.logger.info("已点击开始日期控件")
# # 处理时间选择器设置为2022年9月22日
# if not self.handle_time_selector(2022, 9, 22, original_date):
# self.logger.error("处理时间选择失败")
# return False
# 处理时间选择器,滚动选择年份
if not self.handle_year_selector():
self.logger.error("时间选择器滑动年份,处理时间失败")
return False
return True
except TimeoutException:
self.logger.error("等待更新水准线路按钮可点击超时")
return False
except Exception as e:
self.logger.error(f"更新水准线路时出错: {str(e)}")
return False
def _swipe_year_wheel(self):
"""滑动年份选择器的滚轮"""
try:
# 获取年份选择器滚轮元素
year_wheel = self.driver.find_element(AppiumBy.ID, "com.bjjw.cjgc:id/wheelView1")
# 获取滚轮的位置和尺寸
location = year_wheel.location
size = year_wheel.size
# 计算滚轮中心点坐标
center_x = location['x'] + size['width'] // 2
center_y = location['y'] + size['height'] // 2
# 计算滑动距离 - 滚轮高度的1/5
swipe_distance = size['height'] // 5
# 执行滑动操作 - 从中心向上滑动1/5高度
self.driver.swipe(center_x, center_y - swipe_distance, center_x, center_y, 500)
self.logger.info("已滑动年份选择器")
return True
except Exception as e:
self.logger.error(f"滑动年份选择器时出错: {str(e)}")
return False
def _scroll_to_value(self, picker_id, target_value, original_value, max_attempts=20):
"""滚动选择器到目标值,基于原始值计算滚动次数"""
try:
# 计算需要滚动的次数(绝对值)
scroll_count = abs(int(target_value) - int(original_value))
self.logger.info(f"需要滚动{scroll_count}次将{picker_id}{original_value}调整到{target_value}")
# 确定滚动方向
direction = "down" if int(target_value) > int(original_value) else "up"
# 获取选择器元素
picker = self.wait.until(
EC.visibility_of_element_located((AppiumBy.ID, picker_id))
)
# 计算滚动坐标
x = picker.location['x'] + picker.size['width'] // 2
self.logger.info(f"水平位置x为{x}")
y_center = picker.location['y'] + picker.size['height'] // 2
self.logger.info(f"垂直位置中点y_center为{y_center}")
# start_y = y_center if direction == "down" else picker.location['y']
# end_y = picker.location['y'] if direction == "down" else y_center
# 关键修改:计算选择器高度的五分之一(滑动距离)
height_fifth = picker.size['height'] // 5 # 1/5高度
# 根据方向计算起点和终点,确保滑动距离为 height_fifth
if direction == "down":
# 向下滚动从中心点向上滑动1/5高度
start_y = y_center
end_y = y_center - height_fifth # 终点 = 中心点 - 1/5高度
self.logger.info(f"down垂直开始位置start_y为{y_center}垂直结束位置end_y为{end_y}")
else:
# 向上滚动从中心点向下滑动1/5高度
start_y = y_center
end_y = y_center + height_fifth # 终点 = 中心点 + 1/5高度
self.logger.info(f"up垂直开始位置start_y为{y_center}垂直结束位置end_y为{end_y}")
# 执行滚动操作
for _ in range(scroll_count):
self.driver.swipe(x, start_y, x, end_y, 500)
time.sleep(0.5) # 等待滚动稳定
return True # 循环scroll_count次后直接返回
# # 验证当前值
# current_value = picker.text
# if current_value == str(target_value):
# self.logger.info(f"{picker_id}已达到目标值: {target_value}")
# return True
# 最终验证
# final_value = picker.text
# if final_value == str(target_value):
# self.logger.info(f"{picker_id}已达到目标值: {target_value}")
# return True
# else:
# self.logger.error(f"{picker_id}滚动{scroll_count}次后未达到目标值,当前值: {final_value}")
# return False
except StaleElementReferenceException:
self.logger.warning("元素状态已过期,重新获取")
return False
except Exception as e:
self.logger.error(f"滚动选择器出错: {str(e)}")
return False
def handle_year_selector(self):
"""处理时间选择器,滚动选择年份"""
try:
# 等待时间选择器出现
self.wait.until(
EC.visibility_of_element_located((AppiumBy.ID, ids.ALERT_DIALOG))
)
self.logger.info("时间选择对话框已出现")
# 滚动选择年份
if not self._swipe_year_wheel():
self.logger.error("滚动选择年份失败")
return False
# 点击确认按钮
confirm_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.SCRCOLL_CONFIRM)) # 日期选择器确认按钮
)
confirm_btn.click()
self.logger.info("已确认时间选择")
# 点击对话框确认按钮UPDATE_LEVEL_LINE_CONFIRM
confirm_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.UPDATE_LEVEL_LINE_CONFIRM)) # 选择日期对话框确认按钮
)
confirm_btn.click()
self.logger.info("已点击对话框确认按钮commit")
# 等待加载对话框出现
custom_dialog = self.wait.until(
EC.visibility_of_element_located((AppiumBy.ID, ids.LOADING_DIALOG))
)
self.logger.info("检测到loading对话框出现")
if not check_session_valid(self.driver, self.device_id):
self.logger.warning(f"设备 {self.device_id} 会话无效,尝试重新连接驱动...")
if not reconnect_driver(self.device_id, self.driver):
self.logger.error(f"设备 {self.device_id} 驱动重连失败")
# 新增:等待加载对话框消失(表示更新完成)
WebDriverWait(self.driver, 300).until(
EC.invisibility_of_element_located((AppiumBy.ID, ids.LOADING_DIALOG))
)
self.logger.info("loading对话框已消失更新完成")
return True
except TimeoutException as e:
# 明确超时发生在哪个环节
self.logger.error("处理时间选择时超时:可能是等待对话框出现、日期选择器元素或确认按钮超时", exc_info=True)
return False
except Exception as e:
# 细分不同环节的异常
self.logger.error(f"处理时间选择对话框时出错:可能是日期滚动、选择器确认或对话框确认环节失败 - {str(e)}", exc_info=True)
return False
def handle_time_selector(self, target_year, target_month, target_day, original_date=None):
"""处理时间选择器,选择起始时间并确认"""
self.logger.info(f"传入handle_time_selector的初始日期: {original_date}")
try:
# 等待时间选择器出现
self.wait.until(
EC.visibility_of_element_located((AppiumBy.ID, ids.ALERT_DIALOG))
)
self.logger.info("时间选择对话框已出现")
# 如果没有提供原始日期,使用当前日期控件的值
if not original_date:
original_date = self._get_current_date()
if not original_date:
self.logger.error("无法获取原始日期,处理时间选择失败")
return False
# 滚动选择年份
if not self._scroll_to_value(ids.SCRCOLL_YEAR, target_year, original_date.year):
self.logger.error("滚动选择年份失败")
return False
# 滚动选择月份
if not self._scroll_to_value(ids.SCRCOLL_MONTH, target_month, original_date.month):
self.logger.error("滚动选择月份失败")
return False
# 滚动选择日期
if not self._scroll_to_value(ids.SCRCOLL_DAY, target_day, original_date.day):
self.logger.error("滚动选择日期失败")
return False
# 点击确认按钮
confirm_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.SCRCOLL_CONFIRM)) # 日期选择器确认按钮
)
confirm_btn.click()
self.logger.info("已确认时间选择")
# 点击对话框确认按钮UPDATE_LEVEL_LINE_CONFIRM
confirm_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.UPDATE_LEVEL_LINE_CONFIRM)) # 选择日期对话框确认按钮
)
confirm_btn.click()
self.logger.info("已点击对话框确认按钮commit")
# 新增:等待加载对话框出现
custom_dialog = self.wait.until(
EC.visibility_of_element_located((AppiumBy.ID, ids.LOADING_DIALOG))
)
self.logger.info("检测到loading对话框出现")
if not check_session_valid(self.driver, self.device_id):
self.logger.warning(f"设备 {self.device_id} 会话无效,尝试重新连接驱动...")
if not reconnect_driver(self.device_id, self.driver):
self.logger.error(f"设备 {self.device_id} 驱动重连失败")
# 新增:等待加载对话框消失(表示更新完成)
WebDriverWait(self.driver, 300).until(
EC.invisibility_of_element_located((AppiumBy.ID, ids.LOADING_DIALOG))
)
self.logger.info("loading对话框已消失更新完成")
'''点击commit确认按钮后loading弹窗会出现等待其加载完成后关闭
检测导航栏中的测量tabbar是否出现来确定是否返回True
'''
# measure_tabbar_btn = self.wait.until(
# EC.visibility_of_element_located((AppiumBy.ID, ids.MEASURE_TABBAR_ID))
# )
# self.logger.info("检测测量tabbar按钮出现")
return True
except TimeoutException as e:
# 明确超时发生在哪个环节
self.logger.error("处理时间选择时超时:可能是等待对话框出现、日期选择器元素或确认按钮超时", exc_info=True)
return False
except Exception as e:
# 细分不同环节的异常
self.logger.error(f"处理时间选择对话框时出错:可能是日期滚动、选择器确认或对话框确认环节失败 - {str(e)}", exc_info=True)
return False
def download_tabbar_page_manager(self):
"""执行基础更新操作"""
try:
# 执行基础更新流程
self.logger.info(f"设备 {global_variable.GLOBAL_DEVICE_ID} 开始执行更新流程")
# 点击下载标签栏
if not self.click_download_tabbar():
self.logger.error(f"设备 {global_variable.GLOBAL_DEVICE_ID} 点击下载标签栏失败")
return False
# 更新工作基点
if not self.update_work_base():
self.logger.error(f"设备 {global_variable.GLOBAL_DEVICE_ID} 更新工作基点失败")
return False
# 更新水准线路
if not self.update_level_line():
self.logger.error(f"设备 {global_variable.GLOBAL_DEVICE_ID} 更新水准线路失败")
return False
self.logger.info(f"设备 {global_variable.GLOBAL_DEVICE_ID} 更新操作执行成功")
return True
except Exception as e:
self.logger.error(f"设备 {global_variable.GLOBAL_DEVICE_ID} 执行更新操作时出错: {str(e)}")
return False

179
page_objects/login_page.py Normal file
View File

@@ -0,0 +1,179 @@
# 登录页面操作
# page_objects/login_page.py
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 logging
import time
import subprocess
import globals.ids as ids
import globals.global_variable as global_variable # 导入全局变量模块
import globals.apis as apis
class LoginPage:
def __init__(self, driver, wait):
self.driver = driver
self.wait = wait
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 is_login_page(self):
"""检查当前是否为登录页面"""
try:
return self.driver.find_element(AppiumBy.ID, ids.LOGIN_BTN).is_displayed()
except NoSuchElementException:
return False
def login(self, username=None):
"""执行登录操作"""
try:
logging.info("正在执行登录操作...")
# # 获取文本框中已有的用户名
# username_field = self.wait.until(
# EC.element_to_be_clickable((AppiumBy.ID, ids.LOGIN_USERNAME))
# )
# 获取用户名输入框
username_field = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.LOGIN_USERNAME))
)
# 填写用户名
if username:
# 清空用户名输入框
try:
username_field.clear()
except:
pass
# 填写传入的用户名
username_field.send_keys(username)
existing_username = username
logging.info(f"已填写用户名: {username}")
else:
# 获取文本框中已有的用户名
existing_username = username_field.text
# 日志记录获取到的已有用户名(若为空,也需明确记录,避免后续误解)
if existing_username.strip(): # 去除空格后判断是否有有效内容
logging.info(f"已获取文本框中的已有用户名: {existing_username}")
else:
logging.info("文本框中未检测到已有用户名(内容为空)")
# 将用户名写入全局变量中
global_variable.GLOBAL_USERNAME = existing_username # 关键:给全局变量赋值
# global_variable.set_username(existing_username)
# # 读取文本框内已有的用户名(.text属性获取元素显示的文本内容
# existing_username = username_field.text
# # 3. 将获取到的用户名写入全局变量中
# # global_variable.GLOBAL_USERNAME = existing_username # 关键:给全局变量赋值
# global_variable.set_username(existing_username)
# # 日志记录获取到的已有用户名(若为空,也需明确记录,避免后续误解)
# if existing_username.strip(): # 去除空格后判断是否有有效内容
# logging.info(f"已获取文本框中的已有用户名: {existing_username}")
# else:
# logging.info("文本框中未检测到已有用户名(内容为空)")
# 1. 定位密码输入框
password_field = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.LOGIN_PASSWORD))
)
# 2. 清空密码框(如果需要)
try:
password_field.clear()
# time.sleep(0.5) # 等待清除完成
except:
# 如果clear方法不可用尝试其他方式
pass
accounts = apis.get_accounts_from_server("68ef0e02b0138d25e2ac9918")
matches = [acc for acc in accounts if acc.get("username") == existing_username]
if matches:
password = matches[0].get("password")
password_field.send_keys(password)
# 4. 可选:隐藏键盘
try:
self.driver.hide_keyboard()
except:
pass
# 点击登录按钮
max_retries = 3
retry_count = 0
while retry_count < max_retries:
login_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.LOGIN_BTN))
)
login_btn.click()
logging.info(f"已点击登录按钮 (尝试 {retry_count + 1}/{max_retries})")
# 等待登录完成
time.sleep(3)
# 检查是否登录成功
if self.is_login_successful():
logging.info("登录成功")
return True
else:
logging.warning("登录后未检测到主页面元素,准备重试")
retry_count += 1
if retry_count < max_retries:
logging.info(f"等待2秒后重新尝试登录...")
time.sleep(2)
logging.error(f"登录失败,已尝试 {max_retries}")
return False
except Exception as e:
logging.error(f"登录过程中出错: {str(e)}")
return False
def is_login_successful(self):
"""检查登录是否成功"""
try:
# 等待主页面元素出现
self.wait.until(
EC.presence_of_element_located((AppiumBy.ID, ids.DOWNLOAD_TABBAR_ID))
)
return True
except TimeoutException:
return False

View File

@@ -0,0 +1,403 @@
# 测量标签栏页面操作
# page_objects/measure_tabbar_page.py
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, StaleElementReferenceException
import logging
import time
import globals.ids as ids
import globals.global_variable as global_variable # 导入全局变量模块
from globals.driver_utils import check_session_valid, reconnect_driver, go_main_click_tabber_button # 导入会话检查和重连函数
# import globals.driver_utils as driver_utils # 导入全局变量模块
class MeasureTabbarPage:
def __init__(self, driver, wait,device_id):
self.driver = driver
self.wait = wait
self.logger = logging.getLogger(__name__)
self.seen_items = set() # 记录已经看到的项目,用于检测是否滚动到底部
self.all_items = set() # 记录所有看到的项目,用于检测是否已经查看过所有项目
# 获取设备ID用于重连操作
self.device_id = device_id
def is_measure_tabbar_visible(self):
"""文件列表是否可见"""
try:
return self.driver.find_element(AppiumBy.ID, ids.MEASURE_LIST_ID).is_displayed()
except NoSuchElementException:
self.logger.warning("文件列表未找到")
return False
except Exception as e:
self.logger.error(f"文件列表可见性时发生意外错误: {str(e)}")
return False
def click_measure_tabbar(self):
"""点击测量标签栏"""
try:
measure_tab = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.MEASURE_TABBAR_ID))
)
measure_tab.click()
self.logger.info("已点击测量标签栏")
time.sleep(1)
# 等待测量页面加载完成
self.wait.until(
lambda driver: self.is_measure_tabbar_visible()
)
return True
except TimeoutException:
self.logger.error("等待测量标签栏可点击超时")
return False
except Exception as e:
self.logger.error(f"点击测量标签栏时出错: {str(e)}")
return False
# def scroll_list(self):
# """滑动列表以加载更多项目"""
# try:
# # 获取列表容器
# list_container = self.driver.find_element(AppiumBy.ID, ids.MEASURE_LIST_ID)
# # 计算滑动坐标
# start_x = list_container.location['x'] + list_container.size['width'] // 2
# start_y = list_container.location['y'] + list_container.size['height'] * 0.8
# end_y = list_container.location['y'] + list_container.size['height'] * 0.2
# # 执行滑动
# self.driver.swipe(start_x, start_y, start_x, end_y, 1000)
# self.logger.info("已滑动列表")
# # 等待新内容加载
# time.sleep(2)
# return True
# except Exception as e:
# self.logger.error(f"滑动列表失败: {str(e)}")
# return False
def scroll_list(self, direction="down"):
"""滑动列表以加载更多项目
Args:
direction: 滑动方向,"down"表示向下滑动,"up"表示向上滑动
Returns:
bool: 滑动是否成功执行对于向上滑动如果滑动到顶则返回False
"""
max_retry = 2
retry_count = 0
while retry_count <= max_retry:
# 检查会话是否有效
if not check_session_valid(self.driver, self.device_id):
self.logger.warning(f"会话无效,尝试重新连接... (尝试 {retry_count + 1}/{max_retry})")
try:
# 尝试重新连接驱动
self.driver, self.wait = reconnect_driver(self.device_id, self.driver)
self.logger.info("驱动重新连接成功")
except Exception as reconnect_error:
self.logger.error(f"驱动重新连接失败: {str(reconnect_error)}")
retry_count += 1
if retry_count > max_retry:
self.logger.error("达到最大重试次数,滑动列表失败")
return False
time.sleep(1)
continue
try:
# 获取列表容器
list_container = self.driver.find_element(AppiumBy.ID, ids.MEASURE_LIST_ID)
# 计算滑动坐标
start_x = list_container.location['x'] + list_container.size['width'] // 2
if direction == "down":
# 向下滑动
start_y = list_container.location['y'] + list_container.size['height'] * 0.95
end_y = list_container.location['y'] + list_container.size['height'] * 0.05
else:
# 向上滑动
# 记录滑动前的项目,用于判断是否滑动到顶
before_scroll_items = self.get_current_items()
start_y = list_container.location['y'] + list_container.size['height'] * 0.05
end_y = list_container.location['y'] + list_container.size['height'] * 0.95
# 执行滑动
self.driver.swipe(start_x, start_y, start_x, end_y, 1000)
# 等待新内容加载
time.sleep(1)
# 向上滑动时,检查是否滑动到顶
if direction == "up":
after_scroll_items = self.get_current_items()
# 如果滑动后的项目与滑动前的项目相同,说明已经滑动到顶
if after_scroll_items == before_scroll_items:
self.logger.info("已滑动到列表顶部,列表内容不变")
return False
return True
except Exception as e:
error_msg = str(e)
self.logger.error(f"滑动列表失败: {error_msg}")
# 如果是连接相关的错误,尝试重连
if any(keyword in error_msg.lower() for keyword in ['socket hang up', 'could not proxy command', 'session']):
retry_count += 1
if retry_count > max_retry:
self.logger.error("达到最大重试次数,滑动列表失败")
return False
self.logger.warning(f"尝试重新连接后重试... (尝试 {retry_count}/{max_retry})")
time.sleep(1)
else:
# 非连接错误直接返回False
return False
def get_current_items(self):
"""获取当前页面中的所有项目文本"""
max_retry = 2
retry_count = 0
while retry_count <= max_retry:
# 检查会话是否有效
if not check_session_valid(self.driver, self.device_id):
self.logger.warning(f"会话无效,尝试重新连接... (尝试 {retry_count + 1}/{max_retry})")
try:
# 尝试重新连接驱动
self.driver, self.wait = reconnect_driver(self.device_id, self.driver)
self.logger.info("驱动重新连接成功")
except Exception as reconnect_error:
self.logger.error(f"驱动重新连接失败: {str(reconnect_error)}")
retry_count += 1
if retry_count > max_retry:
self.logger.error("达到最大重试次数,获取当前项目失败")
return []
time.sleep(0.1) # 等待1秒后重试
continue
try:
items = self.driver.find_elements(AppiumBy.ID, ids.MEASURE_LISTVIEW_ID)
item_texts = []
for item in items:
try:
title_element = item.find_element(AppiumBy.ID, ids.MEASURE_NAME_TEXT_ID)
if title_element and title_element.text:
item_texts.append(title_element.text)
except NoSuchElementException:
continue
except Exception as item_error:
self.logger.warning(f"处理项目时出错: {str(item_error)}")
continue
return item_texts
except Exception as e:
error_msg = str(e)
self.logger.error(f"获取当前项目失败: {error_msg}")
# 如果是连接相关的错误,尝试重连
if any(keyword in error_msg.lower() for keyword in ['socket hang up', 'could not proxy command', 'session']):
retry_count += 1
if retry_count > max_retry:
self.logger.error("达到最大重试次数,获取当前项目失败")
return []
self.logger.warning(f"尝试重新连接后重试... (尝试 {retry_count}/{max_retry})")
time.sleep(1)
else:
# 非连接错误,直接返回空列表
return []
def click_item_by_text(self, text):
"""点击指定文本的项目"""
max_retry = 2
retry_count = 0
while retry_count <= max_retry:
# 检查会话是否有效
if not check_session_valid(self.driver, self.device_id):
self.logger.warning(f"会话无效,尝试重新连接... (尝试 {retry_count + 1}/{max_retry})")
try:
# 尝试重新连接驱动
self.driver, self.wait = reconnect_driver(self.device_id, self.driver)
self.logger.info("驱动重新连接成功")
except Exception as reconnect_error:
self.logger.error(f"驱动重新连接失败: {str(reconnect_error)}")
retry_count += 1
if retry_count > max_retry:
self.logger.error("达到最大重试次数,点击项目失败")
return False
time.sleep(1)
continue
try:
# 查找包含指定文本的项目
items = self.driver.find_elements(AppiumBy.ID, ids.MEASURE_LISTVIEW_ID)
for item in items:
try:
title_element = item.find_element(AppiumBy.ID, ids.MEASURE_NAME_TEXT_ID)
if title_element and title_element.text == text:
title_element.click()
self.logger.info(f"已点击项目: {text}")
return True
except NoSuchElementException:
continue
except Exception as item_error:
self.logger.warning(f"处理项目时出错: {str(item_error)}")
continue
self.logger.warning(f"未找到可点击的项目: {text}")
return False
except Exception as e:
error_msg = str(e)
self.logger.error(f"点击项目失败: {error_msg}")
# 如果是连接相关的错误,尝试重连
if any(keyword in error_msg.lower() for keyword in ['socket hang up', 'could not proxy command', 'session']):
retry_count += 1
if retry_count > max_retry:
self.logger.error("达到最大重试次数,点击项目失败")
return False
self.logger.warning(f"尝试重新连接后重试... (尝试 {retry_count}/{max_retry})")
time.sleep(1)
else:
# 非连接错误直接返回False
return False
def find_keyword(self, fixed_filename):
"""查找指定关键词并点击,支持向下和向上滑动查找"""
try:
# 等待线路列表容器出现
self.wait.until(
EC.presence_of_element_located((AppiumBy.ID, ids.MEASURE_LIST_ID))
)
# self.logger.info("线路列表容器已找到")
max_scroll_attempts = 50 # 最大滚动尝试次数
scroll_count = 0
found_items_count = 0 # 记录已找到的项目数量
last_items_count = 0 # 记录上一次找到的项目数量
previous_items = set() # 记录前一次获取的项目集合,用于检测是否到达边界
# 首先尝试向下滑动查找
while scroll_count < max_scroll_attempts:
# 获取当前页面中的所有项目
current_items = self.get_current_items()
# self.logger.info(f"当前页面找到 {len(current_items)} 个项目: {current_items}")
# 检查目标文件是否在当前页面中
if fixed_filename in current_items:
# self.logger.info(f"找到目标文件: {fixed_filename}")
# 点击目标文件
if self.click_item_by_text(fixed_filename):
return True
else:
self.logger.error(f"点击目标文件失败: {fixed_filename}")
return False
# 检查是否到达底部:连续两次获取的项目相同
if current_items == previous_items and len(current_items) > 0:
self.logger.info("连续两次获取的项目相同,已到达列表底部")
break
# 更新前一次项目集合
previous_items = current_items.copy()
# # 记录所有看到的项目
# self.all_items.update(current_items)
# # 检查是否已经查看过所有项目
# if len(current_items) > 0 and found_items_count == len(self.all_items):
# self.logger.info("已向下查看所有项目,未找到目标文件")
# break
# # return False
# found_items_count = len(self.all_items)
# 向下滑动列表以加载更多项目
if not self.scroll_list(direction="down"):
self.logger.error("向下滑动列表失败")
return False
scroll_count += 1
self.logger.info(f"{scroll_count} 次向下滑动,继续查找...")
# 如果向下滑动未找到,尝试向上滑动查找
self.logger.info("向下滑动未找到目标,开始向上滑动查找")
# 重置滚动计数
scroll_count = 0
while scroll_count < max_scroll_attempts:
# 向上滑动列表
# 如果返回False说明已经滑动到顶
if not self.scroll_list(direction="up"):
# 检查是否是因为滑动到顶而返回False
if "已滑动到列表顶部" in self.logger.handlers[0].buffer[-1].message:
self.logger.info("已滑动到列表顶部,停止向上滑动")
break
else:
self.logger.error("向上滑动列表失败")
return False
# 获取当前页面中的所有项目
current_items = self.get_current_items()
# self.logger.info(f"向上滑动后找到 {len(current_items)} 个项目: {current_items}")
# 检查目标文件是否在当前页面中
if fixed_filename in current_items:
self.logger.info(f"找到目标文件: {fixed_filename}")
# 点击目标文件
if self.click_item_by_text(fixed_filename):
return True
else:
self.logger.error(f"点击目标文件失败: {fixed_filename}")
return False
scroll_count += 1
self.logger.info(f"{scroll_count} 次向上滑动,继续查找...")
self.logger.warning(f"经过 {max_scroll_attempts * 2} 次滑动仍未找到目标文件")
return False
except TimeoutException:
self.logger.error("等待线路列表元素超时")
return False
except Exception as e:
self.logger.error(f"查找关键词时出错: {str(e)}")
return False
def measure_tabbar_page_manager(self):
"""执行测量操作"""
try:
# 跳转到测量页面
if not go_main_click_tabber_button(self.driver, self.device_id, "com.bjjw.cjgc:id/img_3_layout"):
logging.error(f"设备 {self.device_id} 跳转到测量页面失败")
return False
# # 点击测量标签栏
# if not self.click_measure_tabbar():
# self.logger.error("点击测量标签栏失败")
# return False
# 固定文件名
fixed_filename = global_variable.GLOBAL_CURRENT_PROJECT_NAME
self.logger.info(f"开始查找测量数据: {fixed_filename}")
# 重置已看到的项目集合
self.seen_items = set()
self.all_items = set()
# 查找并点击测量数据
if self.find_keyword(fixed_filename):
self.logger.info("成功找到并点击测量数据")
return True
else:
self.logger.warning("未找到测量数据")
return False
except Exception as e:
self.logger.error(f"执行测量操作时出错: {str(e)}")
return False

View File

@@ -0,0 +1,343 @@
# test_more_download_page.py
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 logging
import time
class MoreDownloadPage:
def __init__(self, driver, wait,device_id):
self.driver = driver
self.wait = wait
self.device_id = device_id
self.logger = logging.getLogger(__name__)
def is_on_more_download_page(self):
"""通过下载历史数据按钮来判断是否在更多下载页面"""
try:
# 使用下载历史数据按钮的resource-id来检查
download_history_locator = (AppiumBy.ID, "com.bjjw.cjgc:id/download_history")
self.wait.until(EC.presence_of_element_located(download_history_locator))
self.logger.info("已确认在更多下载页面")
return True
except TimeoutException:
self.logger.warning("未找到下载历史数据按钮,不在更多下载页面")
return False
except Exception as e:
self.logger.error(f"检查更多下载页面时发生意外错误: {str(e)}")
return False
def click_download_button(self):
"""点击下载按钮"""
try:
# 点击下载历史数据按钮
download_button = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/download_history"))
)
download_button.click()
self.logger.info("已点击下载历史数据按钮")
# 等待下载操作开始
# time.sleep(3)
return True
except TimeoutException:
self.logger.error("等待下载按钮可点击超时")
return False
except Exception as e:
self.logger.error(f"点击下载按钮时出错: {str(e)}")
return False
def click_download_original_data(self):
"""点击下载原始数据按钮并处理日期选择"""
try:
# 点击下载原始数据按钮
download_original_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/download_org"))
)
download_original_btn.click()
self.logger.info("已点击下载原始数据按钮")
# 等待日期选择弹窗出现
# time.sleep(2)
# 点击选择开始日期
start_date_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/date"))
)
start_date_btn.click()
self.logger.info("已点击选择开始日期")
# 等待日期选择器出现
# time.sleep(2)
# 滑动年份选择器 - 向上滑动1/5的距离
if not self._swipe_year_wheel():
self.logger.error("滑动年份选择器失败")
return False
# 点击日期选择器的确定按钮
confirm_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/okBtn"))
)
confirm_btn.click()
self.logger.info("已确认日期选择")
# 等待日期选择器关闭
# time.sleep(2)
# 假设弹窗有确定按钮,点击它开始下载
try:
# 尝试查找并点击下载弹窗的确定按钮
download_confirm_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((AppiumBy.XPATH, "//android.widget.Button[contains(@text, '确定') or contains(@text, '下载')]"))
)
download_confirm_btn.click()
self.logger.info("已点击下载确认按钮")
except TimeoutException:
self.logger.warning("未找到下载确认按钮,可能不需要确认")
# 等待下载开始
# time.sleep(3)
return True
except TimeoutException:
self.logger.error("等待下载原始数据按钮可点击超时")
return False
except Exception as e:
self.logger.error(f"点击下载原始数据时出错: {str(e)}")
return False
def click_download_result_data(self):
"""点击下载成果数据按钮并处理日期选择"""
try:
# 点击下载成果数据按钮
download_result_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/download_result"))
)
download_result_btn.click()
self.logger.info("已点击下载成果数据按钮")
# 等待日期选择弹窗出现
# time.sleep(2)
# 点击选择开始日期
start_date_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/date"))
)
start_date_btn.click()
self.logger.info("已点击选择开始日期")
# 等待日期选择器出现
# time.sleep(2)
# 滑动年份选择器 - 向上滑动1/5的距离
if not self._swipe_year_wheel():
self.logger.error("滑动年份选择器失败")
return False
# 点击日期选择器的确定按钮
confirm_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/okBtn"))
)
confirm_btn.click()
self.logger.info("已确认日期选择")
# 等待日期选择器关闭
# time.sleep(2)
# 假设弹窗有确定按钮,点击它开始下载
try:
# 尝试查找并点击下载弹窗的确定按钮
download_confirm_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((AppiumBy.XPATH, "//android.widget.Button[contains(@text, '确定') or contains(@text, '下载')]"))
)
download_confirm_btn.click()
self.logger.info("已点击下载确认按钮")
except TimeoutException:
self.logger.warning("未找到下载确认按钮,可能不需要确认")
# 等待下载开始
# time.sleep(3)
return True
except TimeoutException:
self.logger.error("等待下载成果数据按钮可点击超时")
return False
except Exception as e:
self.logger.error(f"点击下载成果数据时出错: {str(e)}")
return False
def _swipe_year_wheel(self):
"""滑动年份选择器的滚轮"""
try:
# 获取年份选择器滚轮元素
year_wheel = self.driver.find_element(AppiumBy.ID, "com.bjjw.cjgc:id/wheelView1")
# 获取滚轮的位置和尺寸
location = year_wheel.location
size = year_wheel.size
# 计算滚轮中心点坐标
center_x = location['x'] + size['width'] // 2
center_y = location['y'] + size['height'] // 2
# 计算滑动距离 - 滚轮高度的1/5
swipe_distance = size['height'] // 5
# 执行滑动操作 - 从中心向上滑动1/5高度
self.driver.swipe(center_x, center_y - swipe_distance, center_x, center_y, 500)
self.logger.info("已滑动年份选择器")
return True
except Exception as e:
self.logger.error(f"滑动年份选择器时出错: {str(e)}")
return False
def wait_for_loading_dialog(self, timeout=900):
"""
检查特定结构的加载弹窗的出现和消失
参数:
timeout: 最大等待时间默认10分钟(600秒)
返回:
bool: 如果加载弹窗出现并消失返回True否则返回False
"""
try:
self.logger.info("开始检查加载弹窗...")
# 首先检查加载弹窗是否出现
start_time = time.time()
loading_appeared = False
# 等待加载弹窗出现最多等待30秒
while time.time() - start_time < 30:
try:
# 根据提供的结构查找加载弹窗
# 查找包含ProgressBar和"loading..."文本的弹窗
loading_indicators = [
(AppiumBy.XPATH, "//android.widget.FrameLayout[@resource-id='android:id/content']/android.widget.LinearLayout[@resource-id='android:id/parentPanel']//android.widget.ProgressBar"),
(AppiumBy.XPATH, "//android.widget.TextView[@resource-id='android:id/message' and @text='loading...']"),
(AppiumBy.XPATH, "//android.widget.FrameLayout[@resource-id='android:id/content']//android.widget.ProgressBar"),
(AppiumBy.XPATH, "//*[contains(@text, 'loading...')]")
]
for by, value in loading_indicators:
try:
element = self.driver.find_element(by, value)
if element.is_displayed():
loading_appeared = True
self.logger.info("数据下载已开始")
self.logger.info("检测到加载弹窗出现")
break
except:
continue
if loading_appeared:
break
except Exception as e:
pass
time.sleep(1)
# 如果加载弹窗没有出现直接返回True
if not loading_appeared:
self.logger.info("未检测到加载弹窗,继续执行")
return True
# 等待加载弹窗消失
self.logger.info("等待加载弹窗消失...")
disappearance_start_time = time.time()
while time.time() - disappearance_start_time < timeout:
try:
# 检查加载弹窗是否还存在
loading_still_exists = False
for by, value in loading_indicators:
try:
element = self.driver.find_element(by, value)
if element.is_displayed():
loading_still_exists = True
break
except:
continue
if not loading_still_exists:
self.logger.info("加载弹窗已消失")
return True
# 每1分钟记录一次状态
if int(time.time() - disappearance_start_time) % 60 == 0:
elapsed_time = int(time.time() - disappearance_start_time)
self.logger.info(f"加载弹窗仍在显示,已等待{elapsed_time//60}分钟")
except Exception as e:
# 如果出现异常,可能弹窗已经消失
self.logger.info("加载弹窗可能已消失")
return True
time.sleep(1)
# 如果超时记录错误并返回False
self.logger.error(f"加载弹窗在{timeout}秒后仍未消失")
return False
except Exception as e:
self.logger.error(f"检查加载弹窗时出错: {str(e)}")
return False
def more_download_page_manager(self):
"""执行更多下载页面管理操作"""
try:
self.logger.info("开始执行更多下载页面操作")
# 检查是否在更多下载页面
if not self.is_on_more_download_page():
self.logger.error("不在更多下载页面")
return False
# 点击下载历史数据按钮
if not self.click_download_button():
self.logger.error("点击下载历史数据按钮失败")
return False
# 等待下载历史数据页面加载完成
# time.sleep(3)
# 点击下载原始数据按钮
if not self.click_download_original_data():
self.logger.error("点击下载原始数据按钮失败")
return False
# 等待下载操作完成
time.sleep(1)
# 使用wait_for_loading_dialog函数等待下载过程中的加载弹窗消失
if not self.wait_for_loading_dialog():
self.logger.warning("下载过程中的加载弹窗未在预期时间内消失,但操作已完成")
# 等待一段时间,确保原始数据下载完成
time.sleep(1)
# 点击下载成果数据按钮
if not self.click_download_result_data():
self.logger.error("点击下载成果数据按钮失败")
return False
# 使用wait_for_loading_dialog函数等待下载过程中的加载弹窗消失
if not self.wait_for_loading_dialog():
self.logger.warning("成果数据下载过程中的加载弹窗未在预期时间内消失,但操作已完成")
self.logger.info("更多下载页面操作执行完成")
return True
except Exception as e:
self.logger.error(f"执行更多下载页面操作时出错: {str(e)}")
return False

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff