# 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"1:第 {current_retry} 次尝试连接失败,剩余 {remaining_times} 次重试机会") else: self.logger.error(f"1:第 {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"2:第 {current_retry} 次尝试连接失败,剩余 {remaining_times} 次重试机会") else: self.logger.error(f"2:第 {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}秒") # 情况1:10秒内未找到按钮(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的LinearLayout(ListView的直接子元素) # # 使用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