Files
cjgc_data/page_objects/section_mileage_config_page.py
2026-03-12 17:03:56 +08:00

1112 lines
49 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.
# page_objects/section_mileage_config_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
from globals import apis, global_variable
from globals.ex_apis import get_weather_simple
import time
import logging
import globals.ids as ids # 导入元素ID
from globals.driver_utils import check_session_valid, reconnect_driver
from check_station import CheckStation
class SectionMileageConfigPage:
def __init__(self, driver, wait, device_id):
self.driver = driver
self.wait = wait
self.device_id = device_id
self.logger = logging.getLogger(__name__)
self.check_station_page = CheckStation(self.driver, self.wait,self.device_id)
def is_on_config_page(self):
"""检查是否在断面里程配置页面"""
try:
title_bar = self.wait.until(
EC.presence_of_element_located((AppiumBy.ID, ids.MEASURE_TITLE_ID))
)
return True
except TimeoutException:
self.logger.warning("未找到断面里程配置页面的标题栏")
return False
def scroll_to_find_element(self, element_id, max_scroll_attempts=5):
"""
下滑页面直到找到指定元素
参数:
element_id: 要查找的元素ID
max_scroll_attempts: 最大下滑次数
返回:
bool: 是否找到元素
"""
try:
for attempt in range(max_scroll_attempts):
# 检查元素是否存在
try:
element = self.driver.find_element(AppiumBy.ID, element_id)
if element.is_displayed():
# self.logger.info(f"找到元素: {element_id}")
return True
except NoSuchElementException:
pass
# 如果没找到,下滑页面
self.logger.info(f"{attempt + 1} 次下滑查找元素: {element_id}")
window_size = self.driver.get_window_size()
start_x = window_size['width'] // 2
self.driver.swipe(start_x, 1600, start_x, 500, 500)
time.sleep(1) # 等待滑动完成
self.logger.warning(f"下滑 {max_scroll_attempts} 次后仍未找到元素: {element_id}")
return False
except Exception as e:
self.logger.error(f"下滑查找元素时出错: {str(e)}")
return False
def scroll_to_find_all_elements(self, element_ids, max_scroll_attempts=10):
"""
下滑页面直到找到所有指定元素
参数:
element_ids: 要查找的元素ID列表
max_scroll_attempts: 最大下滑次数
返回:
bool: 是否找到所有元素
"""
try:
found_count = 0
target_count = len(element_ids)
for attempt in range(max_scroll_attempts):
# 检查每个元素是否存在
current_found = 0
for element_id in element_ids:
try:
element = self.driver.find_element(AppiumBy.ID, element_id)
if element.is_displayed():
current_found += 1
except NoSuchElementException:
pass
if current_found > found_count:
found_count = current_found
self.logger.info(f"找到 {found_count}/{target_count} 个元素")
if found_count == target_count:
self.logger.info(f"成功找到所有 {target_count} 个元素")
return True
# 如果没找到全部,下滑页面
self.logger.info(f"{attempt + 1} 次下滑,已找到 {found_count}/{target_count} 个元素")
window_size = self.driver.get_window_size()
start_x = window_size['width'] // 2
self.driver.swipe(start_x, 1600, start_x, 500, 500)
time.sleep(1) # 等待滑动完成
self.logger.warning(f"下滑 {max_scroll_attempts} 次后仍未找到所有元素,只找到 {found_count}/{target_count}")
return found_count > 0 # 至少找到一个元素也算部分成功
except Exception as e:
self.logger.error(f"下滑查找所有元素时出错: {str(e)}")
return False
def check_work_base_consistency(self):
"""
判断第一个工作基点和最后一个工作基点是否一致
返回:
bool: 是否一致True表示一致False表示不一致
"""
try:
# 获取工作基点路径文本
point_order_element = self.wait.until(
EC.presence_of_element_located((AppiumBy.ID, "com.bjjw.cjgc:id/select_point_order_name"))
)
point_order_text = point_order_element.text
# self.logger.info(f"工作基点路径: {point_order_text}")
# 解析第一个工作基点和最后一个工作基点
if "--->" in point_order_text:
parts = point_order_text.split("--->")
# 提取第一个工作基点
first_base = parts[0].strip()
# 提取最后一个工作基点
last_base = parts[-1].strip()
# self.logger.info(f"第一个工作基点: {first_base}")
# self.logger.info(f"最后一个工作基点: {last_base}")
# 判断是否一致(去除可能的工作基点标识)
first_base_clean = first_base.replace("(工作基点)", "").strip()
last_base_clean = last_base.replace("(工作基点)", "").strip()
is_consistent = first_base_clean == last_base_clean
# self.logger.info(f"工作基点一致性: {is_consistent}")
return is_consistent
else:
self.logger.warning("无法解析工作基点路径格式")
return False
except Exception as e:
self.logger.error(f"检查工作基点一致性时出错: {str(e)}")
return False
def get_observation_type_based_on_base(self):
"""
根据工作基点一致性返回观测类型
返回:
str: 观测类型
"""
try:
is_consistent = self.check_work_base_consistency()
if is_consistent:
obs_type = "往:aBFFB 返:aBFFB"
# self.logger.info("工作基点一致,选择观测类型: 往:aBFFB 返:aBFFB")
else:
obs_type = "aBFFB"
# self.logger.info("工作基点不一致,选择观测类型: aBFFB")
return obs_type
except Exception as e:
self.logger.error(f"获取观测类型时出错: {str(e)}")
return "aBFFB" # 默认值
def select_weather(self, weather_option=""):
"""选择天气"""
try:
# 先下滑查找天气元素
if not self.scroll_to_find_element(ids.MEASURE_WEATHER_ID):
self.logger.error("未找到天气下拉框")
return False
# 点击天气下拉框
weather_spinner = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.MEASURE_WEATHER_ID))
)
weather_spinner.click()
time.sleep(1) # 等待选项弹出
# 等待选择对话框出现
self.wait.until(
EC.presence_of_element_located((AppiumBy.ID, ids.MEASURE_SELECT_ID))
)
# 选择指定天气选项
weather_xpath = f"//android.widget.TextView[@resource-id='{ids.SELECT_DIALOG_TEXT1_ID}' and @text='{weather_option}']"
weather_option_element = self.wait.until(
EC.element_to_be_clickable((AppiumBy.XPATH, weather_xpath))
)
weather_option_element.click()
self.logger.info(f"已选择天气: {weather_option}")
return True
except Exception as e:
self.logger.error(f"选择天气失败: {str(e)}")
return False
def select_observation_type(self, obs_type=None):
"""选择观测类型"""
try:
# 如果未指定观测类型,根据工作基点自动选择
if obs_type is None:
obs_type = self.get_observation_type_based_on_base()
# 先下滑查找观测类型元素
if not self.scroll_to_find_element(ids.MEASURE_TYPE_ID):
self.logger.error("未找到观测类型下拉框")
return False
# 点击观测类型下拉框
obs_type_spinner = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.MEASURE_TYPE_ID))
)
obs_type_spinner.click()
time.sleep(2) # 等待选项弹出
# 等待选择对话框出现
self.wait.until(
EC.presence_of_element_located((AppiumBy.ID, ids.MEASURE_SELECT_ID))
)
# 选择指定观测类型
obs_type_xpath = f"//android.widget.TextView[@resource-id='{ids.SELECT_DIALOG_TEXT1_ID}' and @text='{obs_type}']"
obs_type_option_element = self.wait.until(
EC.element_to_be_clickable((AppiumBy.XPATH, obs_type_xpath))
)
obs_type_option_element.click()
self.logger.info(f"已选择观测类型: {obs_type}")
return True
except Exception as e:
self.logger.error(f"选择观测类型失败: {str(e)}")
return False
def enter_temperature(self, temperature="25"):
"""填写温度"""
try:
# 先下滑查找温度输入框
if not self.scroll_to_find_element(ids.MEASURE_TEMPERATURE_ID):
self.logger.error("未找到温度输入框")
return False
# 找到温度输入框并输入温度值
temp_input = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.MEASURE_TEMPERATURE_ID))
)
temp_input.clear() # 清空原有内容
temp_input.send_keys(temperature)
# self.logger.info(f"已输入温度: {temperature}")
return True
except Exception as e:
self.logger.error(f"输入温度失败: {str(e)}")
return False
def enter_barometric_pressure(self, pressure="800"):
"""填写气压"""
try:
# 先下滑查找气压输入框
if not self.scroll_to_find_element("com.bjjw.cjgc:id/point_list_barometric_et"):
self.logger.error("未找到气压输入框")
return False
# 找到气压输入框并输入气压值
pressure_input = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/point_list_barometric_et"))
)
pressure_input.clear() # 清空原有内容
pressure_input.send_keys(pressure)
# self.logger.info(f"已输入气压: {pressure}")
return True
except Exception as e:
self.logger.error(f"输入气压失败: {str(e)}")
return False
def click_save_button(self):
"""点击保存按钮"""
try:
# 先下滑查找保存按钮
if not self.scroll_to_find_element(ids.MEASURE_SAVE_ID):
self.logger.error("未找到保存按钮")
return False
save_button = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.MEASURE_SAVE_ID))
)
save_button.click()
self.logger.info("已点击保存按钮")
return True
except Exception as e:
self.logger.error(f"点击保存按钮失败: {str(e)}")
return False
def click_conn_level_btn(self):
"""点击连接水准仪按钮"""
try:
conn_level_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.CONNECT_LEVEL_METER))
)
conn_level_btn.click()
self.logger.info("已点击连接水准仪按钮1")
# 等待设备选择对话框出现
self.wait.until(
EC.presence_of_element_located((AppiumBy.ID, "android:id/content"))
)
return True
except Exception as e:
self.logger.error(f"点击连接水准仪按钮失败1: {str(e)}")
return False
def connect_to_device(self):
"""连接设备,处理可能出现的弹窗"""
try:
# 检查已配对设备列表中是否有设备
paired_devices_list_xpath = "//android.widget.ListView[@resource-id='com.bjjw.cjgc:id/paired_devices']"
try:
# 等待配对设备列表出现
paired_list = self.wait.until(
EC.visibility_of_element_located((AppiumBy.XPATH, paired_devices_list_xpath))
)
# 获取列表中的所有子元素(设备项)
device_items = paired_list.find_elements(AppiumBy.CLASS_NAME, "android.widget.TextView")
if device_items:
# 获取第一个设备的文本
first_device_text = device_items[0].text
# self.logger.info(f"找到设备: {first_device_text}")
# 检查第一个设备是否不是"没有已配对的设备"
if "没有已配对的设备" not in first_device_text:
# 存在元素,点击第一个设备
first_device = self.wait.until(
EC.element_to_be_clickable((AppiumBy.XPATH, f"{paired_devices_list_xpath}/android.widget.TextView[1]"))
)
first_device.click()
self.logger.info(f"已点击第一个设备: {first_device_text}")
# 处理可能出现的弹窗
# if self._handle_alert_dialog():
# # 弹窗已处理,重新尝试连接
# self.logger.info("弹窗已处理,重新尝试连接设备")
# return self._retry_connection(paired_devices_list_xpath)
# else:
# # 没有弹窗,正常检查连接状态
# return self._check_connection_status()
# 新增最多尝试3次连接
max_retry_times = 3 # 最大重试次数
current_retry = 0 # 当前重试计数器初始为0代表第1次尝试
while current_retry < max_retry_times:
current_retry += 1 # 每次循环先+1记录当前是第几次尝试
self.logger.info(f"{current_retry} 次尝试连接设备(最多{max_retry_times}次)")
if self._handle_alert_dialog():
# 弹窗已处理,执行本次重试连接
self.logger.info("弹窗已处理,查看按钮状态")
connect_success = self._retry_connection(paired_devices_list_xpath)
# 若本次连接成功立即返回True终止重试
if connect_success:
self.logger.info(f"{current_retry} 次尝试连接成功")
return True
else:
# 本次连接失败,判断是否还有剩余重试次数
remaining_times = max_retry_times - current_retry
if remaining_times > 0:
self.logger.warning(f"{current_retry} 次尝试连接失败,剩余 {remaining_times} 次重试机会")
else:
self.logger.error(f"{current_retry} 次尝试连接失败,已达到最大重试次数({max_retry_times}次)")
else:
# 未检测到弹窗,直接尝试连接(逻辑与原代码一致,仅增加重试计数)
# self.logger.info("未检测到弹窗,尝试连接设备")
# connect_success = self._retry_connection(paired_devices_list_xpath)
connect_success = self._check_connection_status()
if connect_success:
self.logger.info(f"{current_retry} 次尝试连接成功")
return True
else:
remaining_times = max_retry_times - current_retry
if remaining_times > 0:
self.logger.warning(f"{current_retry} 次尝试连接失败,剩余 {remaining_times} 次重试机会")
else:
self.logger.error(f"{current_retry} 次尝试连接失败,已达到最大重试次数({max_retry_times}次)")
# 循环结束3次均失败返回False
return False
else:
self.logger.info("第一个设备是'没有已配对的设备',不点击,等待用户手动连接")
return self._wait_for_manual_connection()
else:
self.logger.info("没有找到已配对设备")
return False
except TimeoutException:
self.logger.info("没有已配对设备;配对设备失败")
return False
except Exception as e:
self.logger.error(f"连接设备过程中出错: {str(e)}")
return False
def _handle_alert_dialog(self):
"""处理连接蓝牙失败警告弹窗"""
try:
# 等待弹窗出现(短暂等待)
alert_dialog = WebDriverWait(self.driver, 5).until(
EC.visibility_of_element_located((AppiumBy.ID, "android:id/content"))
)
# 查找关闭报警按钮
close_alert_btn = WebDriverWait(self.driver, 3).until(
EC.element_to_be_clickable((AppiumBy.XPATH, "//android.widget.Button[@text='关闭报警']"))
)
close_alert_btn.click()
self.logger.info("已点击'关闭报警'按钮")
# # 等待弹窗消失
# WebDriverWait(self.driver, 5).until(
# EC.invisibility_of_element_located((AppiumBy.ID, "android:id/content"))
# )
# self.logger.info("弹窗已关闭")
return True
except TimeoutException:
self.logger.info("未检测到弹窗,继续正常流程")
return False
except Exception as e:
self.logger.error(f"处理弹窗时出错: {str(e)}")
return False
def _retry_connection(self, paired_devices_list_xpath):
"""重新尝试连接设备"""
try:
# 再次点击连接蓝牙设备按钮
connect_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, ids.CONNECT_LEVEL_METER))
)
connect_btn.click()
self.logger.info("已重新点击连接蓝牙设备按钮")
# 等待设备列表重新出现
paired_list = self.wait.until(
EC.visibility_of_element_located((AppiumBy.XPATH, paired_devices_list_xpath))
)
# 获取设备列表并点击第一个设备
device_items = paired_list.find_elements(AppiumBy.CLASS_NAME, "android.widget.TextView")
if device_items and "没有已配对的设备" not in device_items[0].text:
first_device = self.wait.until(
EC.element_to_be_clickable((AppiumBy.XPATH, f"{paired_devices_list_xpath}/android.widget.TextView[1]"))
)
first_device.click()
self.logger.info("已重新点击第一个设备")
# 检查连接状态
return self._check_connection_status()
else:
self.logger.warning("重新尝试时未找到可用设备")
return False
except Exception as e:
self.logger.error(f"重新尝试连接时出错: {str(e)}")
return False
def _check_connection_status(self):
"""检查连接状态"""
try:
# 等待连接状态更新
time.sleep(3)
conn_level_btn = self.driver.find_element(AppiumBy.ID, ids.CONNECT_LEVEL_METER)
if "已连接上" in conn_level_btn.text:
self.logger.info(f"蓝牙设备连接成功: {conn_level_btn.text}")
return True
else:
self.logger.warning(f"蓝牙设备连接失败: {conn_level_btn.text}")
return False
except NoSuchElementException:
self.logger.warning("未找到连接按钮")
return False
def _wait_for_manual_connection(self):
"""等待用户手动连接"""
max_wait_time = 60 # 总最大等待时间600秒
poll_interval = 5 # 每次检查后的休眠间隔30秒
btn_wait_time = 15 # 单个循环内,等待"连接按钮"出现的最大时间15秒
start_time = time.time() # 记录总等待的开始时间戳
while time.time() - start_time < max_wait_time:
conn_level_btn = None # 初始化按钮对象,避免上一轮残留值影响
try:
# 第一步先等待15秒直到按钮出现或超时解决按钮延迟加载问题
conn_level_btn = WebDriverWait(self.driver, btn_wait_time).until(
EC.presence_of_element_located((AppiumBy.ID, ids.CONNECT_LEVEL_METER))
)
self.logger.debug("已检测到连接按钮")
if "已连接上" in conn_level_btn.text:
self.logger.info(f"用户手动连接成功: {conn_level_btn.text}")
return True
else:
# 未连接,计算总剩余等待时间并记录
elapsed_total = time.time() - start_time
remaining_total = max_wait_time - elapsed_total
self.logger.debug(f"等待手动连接中...总剩余时间: {remaining_total:.0f}")
# 情况110秒内未找到按钮btn_wait_time超时
except TimeoutException:
elapsed_total = time.time() - start_time
remaining_total = max_wait_time - elapsed_total
self.logger.warning(f"未检测到连接按钮,{remaining_total:.0f}秒内将再次检查")
# 情况2其他意外异常如驱动异常
except Exception as e:
self.logger.error(f"检查连接状态时出现意外错误: {str(e)}")
# 无论是否找到按钮都休眠poll_interval秒再进入下一轮循环
time.sleep(poll_interval)
self.logger.warning(f"等待{max_wait_time}秒后仍未连接成功,终止等待")
return False
def wait_for_measurement_data(self, timeout=900):
"""
等待并轮询测量数据接口每10秒访问一次直到有数据返回
参数:
timeout: 最大等待时间(秒)
返回:
bool: 是否成功获取到数据
"""
try:
start_time = time.time()
while time.time() - start_time < timeout:
try:
task_data = apis.get_end_with_num()
if not task_data:
# self.logger.info("接口返回但没有数据,继续等待...")
time.sleep(10)
# # 1. 获取屏幕尺寸,计算中心坐标(通用适配所有设备)
# screen_size = self.driver.get_window_size()
# center_x = screen_size['width'] / 2
# center_y = screen_size['height'] / 2
# # 2. 点击屏幕中心点击时长500ms和常规操作一致
# self.driver.tap([(center_x, center_y)], 500)
# self.logger.info(f"已点击屏幕中心(坐标:{center_x}, {center_y}间隔30秒触发")
continue
self.logger.info(f"接口返回数据:{task_data}")
if task_data.get('status') == 3:
self.logger.info("测量任务状态为3测量结束")
return True
else:
self.logger.info("测量任务状态不为3继续等待...")
continue
except Exception as e:
self.logger.error(f"处理接口响应时出错: {str(e)}")
self.logger.error(f"{timeout} 秒内未获取到有效数据")
return False
except Exception as e:
self.logger.error(f"等待测量结束时发生错误: {str(e)}")
return False
def wait_for_measurement_end(self, timeout=900):
"""
等待按钮变成"测量结束"最多15分钟包含驱动重新初始化机制
Args:
timeout: 超时时间默认900秒15分钟
Returns:
bool: 是否成功等到测量结束按钮
"""
try:
# 更新WebDriverWait等待时间为900秒
self.wait = WebDriverWait(self.driver, 900)
self.logger.info(f"等待测量结束按钮出现,最多等待 {timeout}")
start_time = time.time()
reinit_attempts = 0
max_reinit_attempts = 3 # 最大重新初始化次数
while time.time() - start_time < timeout:
try:
# 使用ID查找测量控制按钮
measure_btn = self.driver.find_element(
AppiumBy.ID,
"com.bjjw.cjgc:id/btn_control_begin_or_end"
)
if "测量结束" in measure_btn.text:
self.logger.info("检测到测量结束按钮,暂停两秒等待电脑执行点击任务")
time.sleep(5)
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("驱动重新初始化成功")
else:
self.logger.error("驱动重新初始化失败")
# 继续尝试,而不是立即失败
# 等待一段时间后再次检查
time.sleep(5)
# 每30秒输出一次等待状态
if int(time.time() - start_time) % 30 == 0:
elapsed = int(time.time() - start_time)
self.logger.info(f"已等待 {elapsed} 秒,仍在等待测量结束...")
self.logger.error("等待测量结束按钮超时")
return False
except Exception as e:
self.logger.error(f"等待测量结束时发生严重错误: {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, 20)
self.logger.info(f"设备 {self.device_id} 驱动重新初始化完成")
return True
except Exception as e:
self.logger.error(f"设备 {self.device_id} 驱动重新初始化失败: {str(e)}")
return False
def click_start_measure_btn(self):
"""点击开始测量按钮并处理确认弹窗"""
try:
# 查找并点击开始测量按钮
start_measure_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/btn_control_begin_or_end"))
)
# 检查按钮文本
btn_text = start_measure_btn.text
self.logger.info(f"测量按钮文本: {btn_text}")
if "点击开始测量" in btn_text or "开始测量" in btn_text:
start_measure_btn.click()
self.logger.info("已点击开始测量按钮")
# 处理确认弹窗
dialog_result = self._handle_start_measure_confirm_dialog()
# 如果确认弹窗处理成功,检查线路前测表,查看是否要添加转点
if not dialog_result:
logging.error(f"设备 {self.device_id} 处理开始测量弹窗失败")
return False
else:
self.logger.warning(f"测量按钮状态不是开始测量,当前状态: {btn_text}")
return False
return True
except TimeoutException:
self.logger.error("等待开始测量按钮超时")
return False
except Exception as e:
self.logger.error(f"点击开始测量按钮时出错: {str(e)}")
return False
def add_transition_point(self):
"""添加转点"""
try:
# 查找并点击添加转点按钮
add_transition_btn = self.wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/btn_add_ZPoint"))
)
add_transition_btn.click()
self.logger.info("已点击添加转点按钮")
return True
except TimeoutException:
self.logger.error("等待添加转点按钮超时")
return False
except Exception as e:
self.logger.error(f"添加转点时出错: {str(e)}")
return False
# def chang_status(self):
# """修改断点状态1->2"""
# try:
# # 修改断点状态1->2
# user_name = global_variable.GLOBAL_USERNAME
# line_num = global_variable.GLOBAL_LINE_NUM
# if line_num:
# success = apis.change_breakpoint_status(user_name, line_num, 2)
# if success:
# self.logger.info(f"成功修改断点状态: 线路{line_num} 状态1->2")
# return True
# else:
# self.logger.error(f"修改断点状态失败: 线路{line_num} 状态1->2")
# return False
# else:
# self.logger.warning("未找到线路编码,跳过修改断点状态")
# return False
# except Exception as e:
# self.logger.error(f"修改状态时出错: {str(e)}")
# return False
def _handle_start_measure_confirm_dialog(self):
"""处理开始测量确认弹窗"""
try:
# 等待确认弹窗出现
confirm_dialog = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((AppiumBy.ID, "android:id/content"))
)
self.logger.info("检测到开始测量确认弹窗")
# 检查弹窗标题和消息
try:
title_element = self.driver.find_element(AppiumBy.ID, "android:id/alertTitle")
message_element = self.driver.find_element(AppiumBy.ID, "android:id/message")
self.logger.info(f"弹窗标题: {title_element.text}, 消息: {message_element.text}")
except NoSuchElementException:
self.logger.info("无法获取弹窗详细信息")
# 点击"是"按钮
yes_button = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((AppiumBy.ID, "android:id/button1"))
)
if yes_button.text == "":
yes_button.click()
self.logger.info("已点击''按钮确认开始测量")
# # 等待弹窗消失
# WebDriverWait(self.driver, 5).until(
# EC.invisibility_of_element_located((AppiumBy.ID, "android:id/content"))
# )
self.logger.info("确认弹窗已关闭")
return True
else:
self.logger.error(f"确认按钮文本不是'',实际文本: {yes_button.text}")
return False
except TimeoutException:
self.logger.warning("未检测到开始测量确认弹窗,可能不需要确认")
return True # 没有弹窗也认为是成功的
except Exception as e:
self.logger.error(f"处理开始测量确认弹窗时出错: {str(e)}")
return False
def click_system_back_button(self):
"""点击手机系统返回按钮"""
try:
self.driver.back()
self.logger.info("已点击手机系统返回按钮")
return True
except Exception as e:
self.logger.error(f"点击手机系统返回按钮失败: {str(e)}")
return False
def add_breakpoint_to_tested_list(self):
"""添加测量结束的断点到列表和字典"""
breakpoint_name = global_variable.GLOBAL_CURRENT_PROJECT_NAME
line_num = global_variable.GLOBAL_LINE_NUM
if breakpoint_name and breakpoint_name not in global_variable.GLOBAL_TESTED_BREAKPOINT_LIST:
global_variable.GLOBAL_TESTED_BREAKPOINT_LIST.append(breakpoint_name)
global_variable.GLOBAL_BREAKPOINT_DICT[breakpoint_name] = {
'breakpoint_name': breakpoint_name,
'line_num': line_num
}
logging.info(f"成功添加断点 '{breakpoint_name}' 到上传列表")
logging.info(f"断点详细信息: 线路编码={line_num}")
return True
else:
logging.warning(f"断点名为空或已存在于列表中")
return False
def click_back_button(self):
"""点击手机系统返回按钮"""
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} 驱动重连失败")
try:
self.driver.back()
self.logger.info("已点击手机系统返回按钮")
return True
except Exception as e:
self.logger.error(f"点击手机系统返回按钮失败: {str(e)}")
return False
# def check_listview_stability(self, timeout=20, poll_interval=2, target_count=2):
# """
# 检查列表视图中级2 LinearLayout数量的稳定性
# 参数:
# timeout: 超时时间默认20秒
# poll_interval: 轮询间隔默认2秒
# target_count: 目标数量默认2个
# 返回:
# str:
# - "flash": 20秒内数量无变化
# - "error": 变化后20秒内未达到目标数量
# - "stable": 达到目标数量并保持稳定
# """
# listview_id = "com.bjjw.cjgc:id/auto_data_list"
# start_time = time.time()
# last_count = None
# change_detected = False
# change_time = None
# self.logger.info(f"开始监控列表 {listview_id} 中级2 LinearLayout数量目标数量: {target_count},超时时间: {timeout}秒")
# try:
# while time.time() - start_time < timeout:
# try:
# # 获取当前层级2 LinearLayout数量
# current_count = self._get_level2_linear_layout_count()
# # 首次获取或数量发生变化
# if last_count is None:
# self.logger.info(f"初始层级2 LinearLayout数量: {current_count}")
# last_count = current_count
# change_time = time.time()
# elif current_count != last_count:
# self.logger.info(f"层级2 LinearLayout数量发生变化: {last_count} -> {current_count}")
# last_count = current_count
# change_detected = True
# change_time = time.time()
# # 检查是否达到目标数量
# if current_count >= target_count:
# self.logger.info(f"已达到目标数量 {target_count},继续监控稳定性")
# # 重置计时器,继续监控是否稳定
# start_time = time.time()
# # 检查是否在变化后20秒内未达到目标数量
# if change_detected and change_time and (time.time() - change_time) > timeout:
# if last_count < target_count:
# self.logger.error(f"变化后{timeout}秒内未达到目标数量{target_count},当前数量: {last_count}")
# return "error"
# # 检查是否20秒内无变化
# if change_time and (time.time() - change_time) > timeout:
# self.logger.info(f"层级2 LinearLayout数量在{timeout}秒内无变化返回flash")
# return "flash"
# time.sleep(poll_interval)
# except StaleElementReferenceException:
# self.logger.warning("元素已过时,重新获取")
# continue
# except Exception as e:
# self.logger.error(f"获取层级2 LinearLayout数量时出错: {str(e)}")
# if change_detected and change_time and (time.time() - change_time) > timeout:
# return "error"
# else:
# time.sleep(poll_interval)
# continue
# # 超时处理
# if change_detected:
# if last_count >= target_count:
# self.logger.info(f"已达到目标数量{target_count}并保持稳定")
# return "stable"
# else:
# self.logger.error(f"变化后{timeout}秒内未达到目标数量{target_count},当前数量: {last_count}")
# return "error"
# else:
# self.logger.info(f"层级2 LinearLayout数量在{timeout}秒内无变化返回flash")
# return "flash"
# except Exception as e:
# self.logger.error(f"监控列表稳定性时出错: {str(e)}")
# return "error"
# def _get_level2_linear_layout_count(self):
# """
# 获取层级2 LinearLayout的数量
# 返回:
# int: 层级2 LinearLayout的数量
# """
# try:
# # 定位到ListView
# listview = self.driver.find_element(AppiumBy.ID, "com.bjjw.cjgc:id/auto_data_list")
# # 获取层级2的LinearLayoutListView的直接子元素
# # 使用XPath查找直接子元素
# level2_layouts = listview.find_elements(AppiumBy.XPATH, "./android.widget.LinearLayout")
# count = len(level2_layouts)
# self.logger.debug(f"当前层级2 LinearLayout数量: {count}")
# return count
# except NoSuchElementException:
# self.logger.error(f"未找到列表元素: com.bjjw.cjgc:id/auto_data_list")
# return 0
# except Exception as e:
# self.logger.error(f"获取层级2 LinearLayout数量时出错: {str(e)}")
# return 0
def handle_measurement_dialog(self):
"""处理测量弹窗 - 选择继续测量"""
try:
self.logger.info("检查线路弹出测量弹窗...")
# 直接尝试点击"继续测量"按钮
remeasure_btn = WebDriverWait(self.driver, 2).until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/measure_remeasure_all_btn"))
)
remeasure_btn.click()
self.logger.info("已点击'重新测量'按钮")
try:
WebDriverWait(self.driver, 2).until(
EC.element_to_be_clickable((AppiumBy.ID, "android:id/button1"))
)
except TimeoutException:
self.logger.info("未找到''按钮,可能弹窗未出现")
return False
confirm_btn = WebDriverWait(self.driver, 2).until(
EC.element_to_be_clickable((AppiumBy.ID, "android:id/button1"))
)
confirm_btn.click()
self.logger.info("已点击''按钮")
return True
except TimeoutException:
self.logger.info("未找到继续测量按钮,可能没有弹窗")
return True # 没有弹窗也认为是成功的
except Exception as e:
self.logger.error(f"点击线路测量弹窗按钮时出错: {str(e)}")
return False
def section_mileage_config_page_manager(self, address=None, obs_type=None):
"""执行完整的断面里程配置"""
try:
if not self.handle_measurement_dialog():
self.logger.error("检测到线路测量弹窗,但处理测量弹窗失败")
return False
self.logger.info("检测到线路测量弹窗,且处理测量弹窗成功")
if not self.is_on_config_page():
self.logger.error("不在断面里程配置页面")
return False
# 下滑查找所有必要元素
required_elements = [
ids.MEASURE_WEATHER_ID,
ids.MEASURE_TYPE_ID,
ids.MEASURE_TEMPERATURE_ID,
"com.bjjw.cjgc:id/point_list_barometric_et"
]
if not self.scroll_to_find_all_elements(required_elements):
self.logger.warning("未找到所有必要元素,但继续尝试配置")
# 临时地址
address = apis.get_one_addr(global_variable.GLOBAL_USERNAME) or "四川省资阳市"
# 获取实时天气信息
if address:
weather, temperature, pressure = get_weather_simple(address)
self.logger.info(f"获取到实时天气: {weather}, 温度: {temperature}°C, 气压: {pressure}hPa")
else:
# 使用默认值
weather, temperature, pressure = "", 25.0, 720.0
# 选择天气
if not self.select_weather(weather):
return False
time.sleep(1) # 短暂等待
# 选择观测类型(如果未指定,根据工作基点自动选择)
if not self.select_observation_type(obs_type):
return False
time.sleep(1) # 短暂等待
# 填写温度
if not self.enter_temperature(temperature):
return False
time.sleep(1) # 短暂等待
# 填写气压
if not self.enter_barometric_pressure(pressure):
return False
time.sleep(1) # 短暂等待
# 点击保存
if not self.click_save_button():
return False
# 连接水准仪
if not self.click_conn_level_btn():
return False
# 连接蓝牙设备
if not self.connect_to_device():
return False
# 连接设备成功,要点击“点击开始测量”
if not self.click_start_measure_btn():
return False
# if not self.check_station_page.run():
# self.logger.error("检查站页面运行失败")
# return False
# # 添加断点到列表
# if not self.add_breakpoint_to_tested_list():
# return False
# # 点击返回按钮
# if not self.click_back_button():
# return False
# # 测量结束。点击手机物理返回按钮,返回测量页面
# # 点击了手机独步导航栏返回键
# if not self.click_system_back_button():
# return False
self.logger.info("断面里程配置完成,正在执行测量")
return True
except Exception as e:
self.logger.error(f"断面里程配置过程中出错: {str(e)}")
return False