first commit
This commit is contained in:
BIN
globals/__pycache__/apis.cpython-312.pyc
Normal file
BIN
globals/__pycache__/apis.cpython-312.pyc
Normal file
Binary file not shown.
BIN
globals/__pycache__/create_link.cpython-312.pyc
Normal file
BIN
globals/__pycache__/create_link.cpython-312.pyc
Normal file
Binary file not shown.
BIN
globals/__pycache__/driver_utils.cpython-312.pyc
Normal file
BIN
globals/__pycache__/driver_utils.cpython-312.pyc
Normal file
Binary file not shown.
BIN
globals/__pycache__/ex_apis.cpython-312.pyc
Normal file
BIN
globals/__pycache__/ex_apis.cpython-312.pyc
Normal file
Binary file not shown.
BIN
globals/__pycache__/global_variable.cpython-312.pyc
Normal file
BIN
globals/__pycache__/global_variable.cpython-312.pyc
Normal file
Binary file not shown.
BIN
globals/__pycache__/ids.cpython-312.pyc
Normal file
BIN
globals/__pycache__/ids.cpython-312.pyc
Normal file
Binary file not shown.
520
globals/apis.py
Normal file
520
globals/apis.py
Normal file
@@ -0,0 +1,520 @@
|
||||
import requests
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
from typing import Optional, Dict, Any
|
||||
import globals.global_variable as global_variable
|
||||
|
||||
def send_tcp_command(command="StartMultiple", host="127.0.0.1", port=8888, timeout=10):
|
||||
"""
|
||||
使用TCP协议发送命令到指定地址和端口
|
||||
|
||||
参数:
|
||||
command: 要发送的命令字符串(默认:"StartMultiple")
|
||||
host: 目标主机地址(默认:"127.0.0.1")
|
||||
port: 目标端口(默认:8888)
|
||||
timeout: 连接超时时间(秒,默认:10)
|
||||
|
||||
返回:
|
||||
成功返回服务器响应(字符串),失败返回None
|
||||
"""
|
||||
# 创建TCP套接字
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
try:
|
||||
# 设置超时时间
|
||||
sock.settimeout(timeout)
|
||||
|
||||
# 连接到目标服务器
|
||||
sock.connect((host, port))
|
||||
logging.info(f"已成功连接到 {host}:{port}")
|
||||
|
||||
# 发送命令(注意:需要根据服务器要求的编码格式发送,这里用UTF-8)
|
||||
sock.sendall(command.encode('utf-8'))
|
||||
logging.info(f"已发送命令: {command}")
|
||||
|
||||
# 接收服务器响应(缓冲区大小1024字节,可根据实际情况调整)
|
||||
response = sock.recv(1024)
|
||||
if response:
|
||||
response_str = response.decode('utf-8')
|
||||
logging.info(f"收到响应: {response_str}")
|
||||
return response_str
|
||||
else:
|
||||
logging.info("未收到服务器响应")
|
||||
return None
|
||||
|
||||
except ConnectionRefusedError:
|
||||
logging.info(f"连接被拒绝,请检查 {host}:{port} 是否开启服务")
|
||||
return None
|
||||
except socket.timeout:
|
||||
logging.info(f"连接超时({timeout}秒)")
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.info(f"发送命令时发生错误: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_breakpoint_list():
|
||||
"""
|
||||
获取需要处理的断点列表
|
||||
"""
|
||||
# 请求参数
|
||||
params = {
|
||||
'user_name': global_variable.GLOBAL_USERNAME
|
||||
}
|
||||
|
||||
# 请求地址
|
||||
url = "https://engineering.yuxindazhineng.com/index/index/get_name_all"
|
||||
|
||||
try:
|
||||
# 发送GET请求
|
||||
response = requests.get(url, params=params, timeout=30)
|
||||
|
||||
# 检查请求是否成功
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
|
||||
# 检查接口返回状态
|
||||
if result.get('code') == 0:
|
||||
data = result.get('data', [])
|
||||
logging.info("成功获取断点列表,数据条数:", len(data))
|
||||
|
||||
# 打印断点信息
|
||||
# for item in data:
|
||||
# logging.info(f"线路编码: {item.get('line_num')}, "
|
||||
# f"线路名称: {item.get('line_name')}, "
|
||||
# f"状态: {item.get('status')}, "
|
||||
# f"用户: {item.get('name')}")
|
||||
|
||||
return data
|
||||
else:
|
||||
logging.info(f"接口返回错误: {result.get('code')}")
|
||||
return [{"id": 37,
|
||||
"user_name": "wangshun",
|
||||
"name": "wangshun",
|
||||
"line_num": "L193588",
|
||||
"line_name": "CDWZQ-2标-155号路基左线-461221-461570-155左-平原",
|
||||
"status": 3
|
||||
}]
|
||||
else:
|
||||
logging.info(f"请求失败,状态码: {response.status_code}")
|
||||
return []
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.info(f"请求异常: {e}")
|
||||
return []
|
||||
except ValueError as e:
|
||||
logging.info(f"JSON解析错误: {e}")
|
||||
return []
|
||||
|
||||
def filter_breakpoint_list_by_status(status_codes):
|
||||
"""
|
||||
根据状态码过滤断点列表,只保留line_name
|
||||
|
||||
Args:
|
||||
status_codes: 状态码列表,如 [0, 1] 或 [0, 1, 2, 3]
|
||||
|
||||
Returns:
|
||||
list: 包含line_name的列表
|
||||
"""
|
||||
data = get_breakpoint_list()
|
||||
|
||||
if not data:
|
||||
logging.info("获取断点列表失败或列表为空")
|
||||
return []
|
||||
|
||||
# 根据状态码过滤数据
|
||||
if status_codes:
|
||||
filtered_data = [item for item in data if item.get('status') in status_codes]
|
||||
logging.info(f"过滤后的断点数量 (状态{status_codes}): {len(filtered_data)}")
|
||||
|
||||
# 按状态分组显示
|
||||
for status in status_codes:
|
||||
status_count = len([item for item in filtered_data if item.get('status') == status])
|
||||
logging.info(f"状态{status}的断点数量: {status_count}")
|
||||
else:
|
||||
# 如果没有指定状态码,返回所有数据
|
||||
filtered_data = data
|
||||
logging.info("未指定状态码,返回所有数据")
|
||||
return filtered_data
|
||||
|
||||
def get_measurement_task():
|
||||
"""
|
||||
获取测量任务
|
||||
返回: 如果有状态为1的数据返回任务信息,否则返回None
|
||||
"""
|
||||
try:
|
||||
url = "https://engineering.yuxindazhineng.com/index/index/getOne"
|
||||
|
||||
# 获取用户名
|
||||
user_name = global_variable.GLOBAL_USERNAME
|
||||
if not user_name:
|
||||
logging.error("未设置用户名,无法获取测量任务")
|
||||
return None
|
||||
|
||||
# 构造请求参数
|
||||
data = {
|
||||
"user_name": user_name
|
||||
}
|
||||
|
||||
logging.info(f"请求参数: user_name={user_name}")
|
||||
response = requests.post(url, data=data, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
logging.info(f"接口返回数据: {data}")
|
||||
|
||||
if data.get('code') == 0 and data.get('data'):
|
||||
task_data = data['data']
|
||||
if task_data.get('status') == 1:
|
||||
logging.info(f"获取到测量任务: {task_data}")
|
||||
return task_data
|
||||
else:
|
||||
logging.info("获取到的任务状态不为1,不执行测量")
|
||||
return None
|
||||
else:
|
||||
logging.warning("未获取到有效任务数据")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取测量任务失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_end_with_num():
|
||||
"""
|
||||
根据线路编码获取测量任务
|
||||
返回: 如果有状态为1的数据返回任务信息,否则返回None
|
||||
"""
|
||||
try:
|
||||
url = "https://engineering.yuxindazhineng.com/index/index/getOne3"
|
||||
|
||||
# 获取用户名
|
||||
user_name = global_variable.GLOBAL_USERNAME
|
||||
line_num = global_variable.GLOBAL_LINE_NUM
|
||||
if not line_num:
|
||||
logging.error("未设置线路编码,无法获取测量任务")
|
||||
return None
|
||||
if not user_name:
|
||||
logging.error("未设置用户名,无法获取测量任务")
|
||||
return None
|
||||
|
||||
# 构造请求参数
|
||||
data = {
|
||||
"user_name": user_name,
|
||||
"line_num": line_num
|
||||
}
|
||||
|
||||
# logging.info(f"请求参数: user_name={user_name}, line_num={line_num}")
|
||||
response = requests.post(url, data=data, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
logging.info(f"接口返回数据: {data}")
|
||||
|
||||
if data.get('code') == 0 and data.get('data'):
|
||||
task_data = data['data']
|
||||
if task_data.get('status') == 3:
|
||||
logging.info(f"获取到测量任务: {task_data}")
|
||||
return task_data
|
||||
else:
|
||||
logging.info("获取到的任务状态不为3,不执行测量")
|
||||
return None
|
||||
else:
|
||||
# logging.warning("未获取到有效任务数据")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取测量任务失败: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def change_breakpoint_status(user_name, line_num, status):
|
||||
"""
|
||||
修改断点状态
|
||||
|
||||
Args:
|
||||
user_name: 登录账号名
|
||||
line_num: 线路编码
|
||||
status: 当前工作状态,0未开始 1操作中 2操作完成
|
||||
|
||||
Returns:
|
||||
bool: 操作是否成功
|
||||
"""
|
||||
try:
|
||||
url = "https://engineering.yuxindazhineng.com/index/index/change"
|
||||
data = {
|
||||
"user_name": user_name,
|
||||
"line_num": line_num,
|
||||
"status": status
|
||||
}
|
||||
|
||||
response = requests.post(url, data=data, timeout=10)
|
||||
result = response.json()
|
||||
|
||||
if result.get("code") == 0:
|
||||
logging.info(f"修改断点状态成功: 线路{line_num} 状态{status} - {result.get('msg')}")
|
||||
return True
|
||||
else:
|
||||
logging.error(f"修改断点状态失败: 线路{line_num} 状态{status} - {result.get('msg')}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"修改断点状态请求异常: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
def get_one_addr(user_name):
|
||||
"""
|
||||
根据用户名获取一个地址信息
|
||||
|
||||
Args:
|
||||
user_name (str): 登录用户名
|
||||
|
||||
Returns:
|
||||
dict: API的原始响应数据
|
||||
"""
|
||||
# 请求地址
|
||||
url = "https://engineering.yuxindazhineng.com/index/index/getOneAddr"
|
||||
|
||||
# 请求参数
|
||||
data = {
|
||||
"user_name": user_name
|
||||
}
|
||||
|
||||
# 请求头
|
||||
headers = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
try:
|
||||
# 发送POST请求
|
||||
response = requests.post(url,data=data,timeout=10)
|
||||
|
||||
# 检查请求是否成功
|
||||
response.raise_for_status()
|
||||
|
||||
# 解析返回数据的地址
|
||||
addr = response.json().get('data').get('addr')
|
||||
if addr:
|
||||
logging.info(f"获取到地址: {addr}")
|
||||
else:
|
||||
logging.warning("返回数据中未包含地址信息")
|
||||
|
||||
# 直接返回API的响应
|
||||
return addr
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
# 返回错误信息
|
||||
return False
|
||||
except json.JSONDecodeError as e:
|
||||
return False
|
||||
|
||||
def get_work_conditions_by_linecode(linecode: str) -> Optional[Dict[str, Dict]]:
|
||||
"""
|
||||
通过线路编码获取工况信息
|
||||
|
||||
Args:
|
||||
linecode: 线路编码,如 "L118134"
|
||||
|
||||
Returns:
|
||||
返回字典,格式为 {point_id: {"sjName": "", "workinfoname": "", "work_type": ""}}
|
||||
如果请求失败返回None
|
||||
"""
|
||||
url="http://www.yuxindazhineng.com:3002/api/comprehensive_data/get_settlement_by_linecode"
|
||||
max_retries = 3 # 最大重试次数
|
||||
retry_count = 0 # 当前重试计数
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
# 准备请求参数
|
||||
payload = {"linecode": linecode}
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
}
|
||||
|
||||
logging.info(f"发送POST请求到: {url}")
|
||||
logging.info(f"请求参数: {payload}")
|
||||
|
||||
# 发送POST请求
|
||||
response = requests.post(
|
||||
url,
|
||||
json=payload,
|
||||
headers=headers,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
# 检查响应状态
|
||||
if response.status_code != 200:
|
||||
logging.error(f"HTTP请求失败,状态码: {response.status_code}")
|
||||
retry_count += 1
|
||||
if retry_count < max_retries:
|
||||
logging.info(f"准备重试... (剩余 {max_retries - retry_count} 次)")
|
||||
continue # 继续重试
|
||||
|
||||
# 解析响应数据
|
||||
try:
|
||||
result = response.json()
|
||||
except json.JSONDecodeError as e:
|
||||
logging.error(f"JSON解析失败: {str(e)}")
|
||||
retry_count += 1
|
||||
if retry_count < max_retries:
|
||||
logging.info(f"准备重试... (剩余 {max_retries - retry_count} 次)")
|
||||
continue # 继续重试
|
||||
|
||||
|
||||
# 检查API返回码
|
||||
if result.get('code') != 0:
|
||||
logging.error(f"API返回错误: {result.get('message', '未知错误')}")
|
||||
return None
|
||||
|
||||
# 提取数据
|
||||
data_list = result.get('data', [])
|
||||
if not data_list:
|
||||
logging.warning("未找到工况数据")
|
||||
return {}
|
||||
|
||||
# 处理数据,提取所需字段
|
||||
work_conditions = {}
|
||||
for item in data_list:
|
||||
point_id = item.get('aname')
|
||||
if point_id:
|
||||
work_conditions[point_id] = {
|
||||
"sjName": item.get('sjName', ''),
|
||||
"workinfoname": item.get('workinfoname', ''),
|
||||
"work_type": item.get('work_type', '')
|
||||
}
|
||||
|
||||
logging.info(f"成功提取 {len(work_conditions)} 个测点的工况信息")
|
||||
return work_conditions
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.error(f"网络请求异常: {str(e)}")
|
||||
retry_count += 1
|
||||
if retry_count < max_retries:
|
||||
logging.info(f"准备重试... (剩余 {max_retries - retry_count} 次)")
|
||||
except json.JSONDecodeError as e:
|
||||
logging.error(f"JSON解析失败: {str(e)}")
|
||||
retry_count += 1
|
||||
if retry_count < max_retries:
|
||||
logging.info(f"准备重试... (剩余 {max_retries - retry_count} 次)")
|
||||
except Exception as e:
|
||||
logging.error(f"获取工况信息时发生未知错误: {str(e)}")
|
||||
retry_count += 1
|
||||
if retry_count < max_retries:
|
||||
logging.info(f"准备重试... (剩余 {max_retries - retry_count} 次)")
|
||||
# 达到最大重试次数仍失败
|
||||
logging.error(f"已达到最大重试次数 ({max_retries} 次),请求失败")
|
||||
return None
|
||||
|
||||
def get_user_max_variation(username: str) -> Optional[int]:
|
||||
"""
|
||||
调用POST接口根据用户名获取用户的max_variation信息
|
||||
|
||||
Args:
|
||||
username: 目标用户名,如 "chzq02-02guoyu"
|
||||
|
||||
Returns:
|
||||
成功:返回用户的max_variation整数值
|
||||
失败:返回None
|
||||
"""
|
||||
# 接口基础配置
|
||||
api_url = "http://www.yuxindazhineng.com:3002/api/accounts/get"
|
||||
timeout = 30 # 超时时间(避免请求长时间阻塞)
|
||||
|
||||
# 1. 准备请求参数与头部
|
||||
# 接口要求的POST参数(JSON格式)
|
||||
payload = {"username": username}
|
||||
# 请求头部:指定JSON格式,模拟浏览器UA避免被接口拦截
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
}
|
||||
|
||||
try:
|
||||
# 2. 发送POST请求
|
||||
logging.info(f"向接口 {api_url} 发送请求,查询用户名:{username}")
|
||||
response = requests.post(
|
||||
url=api_url,
|
||||
json=payload, # 自动将字典转为JSON字符串,无需手动json.dumps()
|
||||
headers=headers,
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
# 3. 检查HTTP响应状态(200表示请求成功到达服务器)
|
||||
response.raise_for_status() # 若状态码非200(如404、500),直接抛出HTTPError
|
||||
logging.info(f"接口请求成功,HTTP状态码:{response.status_code}")
|
||||
|
||||
# 4. 解析JSON响应(处理文档中提到的"网页解析失败"风险)
|
||||
try:
|
||||
response_data = response.json()
|
||||
except json.JSONDecodeError as e:
|
||||
logging.error(f"接口返回数据非JSON格式,解析失败:{str(e)}")
|
||||
logging.error(f"接口原始返回内容:{response.text[:500]}") # 打印前500字符便于排查
|
||||
return None
|
||||
|
||||
# 5. 检查接口业务逻辑是否成功(按需求中"code=0表示查询成功")
|
||||
if response_data.get("code") != 0:
|
||||
logging.error(f"接口查询失败,业务错误信息:{response_data.get('message', '未知错误')}")
|
||||
return None
|
||||
|
||||
# 6. 验证返回数据结构并提取max_variation
|
||||
data_list = response_data.get("data", [])
|
||||
if not data_list:
|
||||
logging.warning(f"查询到用户名 {username},但未返回账号数据")
|
||||
return None
|
||||
|
||||
# 检查第一条数据是否包含max_variation
|
||||
first_user = data_list[0]
|
||||
if "max_variation" not in first_user:
|
||||
logging.warning(f"用户 {username} 的返回数据中缺少 max_variation 字段")
|
||||
return None
|
||||
|
||||
max_variation = first_user["max_variation"]
|
||||
logging.info(f"成功查询到用户 {username} 的 max_variation:{max_variation}")
|
||||
|
||||
# 7. 直接返回max_variation的值
|
||||
return max_variation
|
||||
|
||||
# 处理请求过程中的异常(网络问题、超时等)
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.error(f"接口请求异常(网络/超时/服务器不可达):{str(e)}")
|
||||
# 若为连接错误,提示检查文档中提到的"不支持的网页类型"或域名有效性
|
||||
if "ConnectionRefusedError" in str(e) or "Failed to establish a new connection" in str(e):
|
||||
logging.error(f"建议排查:1. 接口域名 {api_url} 是否可访问;2. 服务器是否正常运行;3. 端口3002是否开放")
|
||||
return None
|
||||
|
||||
# 处理其他未知异常
|
||||
except Exception as e:
|
||||
logging.error(f"获取用户 {username} 的 max_variation 时发生未知错误:{str(e)}")
|
||||
return None
|
||||
|
||||
def get_accounts_from_server(yh_id):
|
||||
"""从服务器获取账户信息"""
|
||||
url = "http://www.yuxindazhineng.com:3002/api/accounts/get_uplaod_data"
|
||||
headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
data = {
|
||||
"yh_id": yh_id
|
||||
}
|
||||
|
||||
try:
|
||||
print(f"🔍 查询服务器账户信息,用户ID: {yh_id}")
|
||||
response = requests.post(url, headers=headers, json=data, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("code") == 0:
|
||||
print(f"✅ 查询成功,找到 {result.get('total', 0)} 个账户")
|
||||
return result.get("data", [])
|
||||
else:
|
||||
print(f"❌ 查询失败: {result.get('message', '未知错误')}")
|
||||
return []
|
||||
else:
|
||||
print(f"❌ 服务器响应错误: {response.status_code}")
|
||||
return []
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"❌ 网络请求失败: {e}")
|
||||
return []
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"❌ JSON解析失败: {e}")
|
||||
return []
|
||||
273
globals/create_link.py
Normal file
273
globals/create_link.py
Normal file
@@ -0,0 +1,273 @@
|
||||
import subprocess
|
||||
import re
|
||||
import time
|
||||
import requests
|
||||
import json
|
||||
from appium import webdriver
|
||||
from appium.webdriver.common.appiumby import AppiumBy
|
||||
from appium.options.android import UiAutomator2Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from urllib3.connection import port_by_scheme
|
||||
|
||||
|
||||
# =======================
|
||||
# 基础工具函数
|
||||
# =======================
|
||||
|
||||
def run_command(command):
|
||||
"""执行系统命令并返回输出"""
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
# =======================
|
||||
# 无线ADB连接管理
|
||||
# =======================
|
||||
|
||||
def check_wireless_connections(target_port=4723):
|
||||
"""
|
||||
检查当前无线ADB连接状态
|
||||
返回: (list) 当前无线连接的设备列表,每个元素为(device_id, ip, port)
|
||||
"""
|
||||
devices_output = run_command("adb devices")
|
||||
lines = devices_output.splitlines()[1:]
|
||||
|
||||
wireless_connections = []
|
||||
|
||||
for line in lines:
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
parts = line.split()
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
|
||||
device_id = parts[0]
|
||||
status = parts[1]
|
||||
|
||||
# 检查是否为无线连接(包含:端口)
|
||||
if ":" in device_id and status == "device":
|
||||
# 解析IP和端口
|
||||
ip_port = device_id.split(":")
|
||||
if len(ip_port) == 2:
|
||||
ip, port = ip_port[0], ip_port[1]
|
||||
wireless_connections.append((device_id, ip, int(port)))
|
||||
|
||||
return wireless_connections
|
||||
|
||||
def disconnect_wireless_connection(connection_id):
|
||||
"""断开指定的无线ADB连接"""
|
||||
print(f" 断开连接: {connection_id}")
|
||||
result = run_command(f"adb disconnect {connection_id}")
|
||||
return result
|
||||
|
||||
def cleanup_wireless_connections(target_device_ip=None, target_port=4723):
|
||||
"""
|
||||
清理无线ADB连接
|
||||
- 如果target_device_ip为None:断开所有端口为4723的连接
|
||||
- 如果target_device_ip有值:断开所有端口为4723且IP不是目标设备的连接
|
||||
返回: (bool) 是否需要建立新连接
|
||||
"""
|
||||
print("\n🔍 检查无线ADB连接状态...")
|
||||
|
||||
# 获取当前所有无线连接
|
||||
wireless_connections = check_wireless_connections(target_port)
|
||||
|
||||
if not wireless_connections:
|
||||
print("📡 当前没有无线ADB连接")
|
||||
return True # 需要建立新连接
|
||||
|
||||
print(f"📡 发现 {len(wireless_connections)} 个无线连接:")
|
||||
for conn_id, ip, port in wireless_connections:
|
||||
print(f" - {conn_id} (IP: {ip}, 端口: {port})")
|
||||
|
||||
need_new_connection = True
|
||||
connections_to_disconnect = []
|
||||
|
||||
for conn_id, ip, port in wireless_connections:
|
||||
# 检查端口是否为4723
|
||||
if port != target_port:
|
||||
print(f" ⚠️ 连接 {conn_id} 端口不是 {target_port},保持不动")
|
||||
continue
|
||||
|
||||
# 如果没有指定目标IP,断开所有4723端口的连接
|
||||
if target_device_ip is None:
|
||||
connections_to_disconnect.append(conn_id)
|
||||
continue
|
||||
|
||||
# 如果指定了目标IP,检查IP是否匹配
|
||||
if ip == target_device_ip:
|
||||
print(f" ✅ 发现目标设备的连接: {conn_id}")
|
||||
need_new_connection = False # 已有正确连接,不需要新建
|
||||
else:
|
||||
print(f" ⚠️ 发现其他设备的4723端口连接: {conn_id}")
|
||||
connections_to_disconnect.append(conn_id)
|
||||
|
||||
# 断开需要清理的连接
|
||||
for conn_id in connections_to_disconnect:
|
||||
disconnect_wireless_connection(conn_id)
|
||||
time.sleep(1) # 等待断开完成
|
||||
|
||||
# 如果断开了一些连接,重新检查状态
|
||||
if connections_to_disconnect:
|
||||
print("🔄 重新检查连接状态...")
|
||||
time.sleep(2)
|
||||
remaining = check_wireless_connections(target_port)
|
||||
if remaining:
|
||||
for conn_id, ip, port in remaining:
|
||||
if ip == target_device_ip and port == target_port:
|
||||
print(f" ✅ 目标设备连接仍然存在: {conn_id}")
|
||||
need_new_connection = False
|
||||
break
|
||||
|
||||
return need_new_connection
|
||||
|
||||
|
||||
# =======================
|
||||
# Appium 启动
|
||||
# =======================
|
||||
|
||||
def start_appium():
|
||||
appium_port = 4723
|
||||
print(f"🚀 启动 Appium Server(端口 {appium_port})...")
|
||||
subprocess.Popen(
|
||||
["appium.cmd", "-a", "127.0.0.1", "-p", str(appium_port)],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
# 检查端口是否就绪(替代固定sleep)
|
||||
max_wait = 30 # 最大等待30秒
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < max_wait:
|
||||
try:
|
||||
# 尝试连接Appium端口,验证是否就绪
|
||||
import socket
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(1)
|
||||
result = sock.connect_ex(("127.0.0.1", appium_port))
|
||||
sock.close()
|
||||
if result == 0: # 端口就绪
|
||||
print(f"✅ Appium Server 启动成功(端口 {appium_port})")
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(1)
|
||||
|
||||
print(f"❌ Appium Server 启动超时({max_wait}秒)")
|
||||
return False
|
||||
|
||||
|
||||
# =======================
|
||||
# 无线 ADB 建链主流程
|
||||
# =======================
|
||||
|
||||
def setup_adb_wireless():
|
||||
target_port = 4723
|
||||
print(f"🚀 开始无线 ADB 建链(端口 {target_port})")
|
||||
|
||||
# 获取USB连接的设备
|
||||
devices_output = run_command("adb devices")
|
||||
lines = devices_output.splitlines()[1:]
|
||||
|
||||
usb_devices = []
|
||||
|
||||
for line in lines:
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
parts = line.split()
|
||||
if len(parts) < 2 or parts[1] != "device":
|
||||
continue
|
||||
|
||||
device_id = parts[0]
|
||||
|
||||
# 跳过已经是无线的
|
||||
if ":" in device_id:
|
||||
continue
|
||||
|
||||
usb_devices.append(device_id)
|
||||
|
||||
if not usb_devices:
|
||||
print("❌ 未检测到 USB 设备")
|
||||
return
|
||||
|
||||
for serial in usb_devices:
|
||||
print(f"\n🔎 处理设备: {serial}")
|
||||
|
||||
# 获取WLAN IP
|
||||
ip_info = run_command(f"adb -s {serial} shell ip addr show wlan0")
|
||||
ip_match = re.search(r'inet\s+(\d+\.\d+\.\d+\.\d+)', ip_info)
|
||||
|
||||
if not ip_match:
|
||||
print("⚠️ 获取 IP 失败,请确认已连接 WiFi")
|
||||
continue
|
||||
|
||||
device_ip = ip_match.group(1)
|
||||
print(f"📍 设备 IP: {device_ip}")
|
||||
|
||||
# ===== 清理现有无线连接 =====
|
||||
need_new_connection = cleanup_wireless_connections(
|
||||
target_device_ip=device_ip,
|
||||
target_port=target_port
|
||||
)
|
||||
|
||||
# ===== 建立新连接(如果需要) =====
|
||||
if need_new_connection:
|
||||
print(f"\n🔌 建立新的无线连接: {device_ip}:{target_port}")
|
||||
|
||||
# 切 TCP 模式
|
||||
print(f" 设置设备 {serial} 为 TCP 模式,端口 {target_port}...")
|
||||
run_command(f"adb -s {serial} tcpip {target_port}")
|
||||
time.sleep(3) # 等待模式切换
|
||||
|
||||
# 无线连接
|
||||
connect_result = run_command(f"adb connect {device_ip}:{target_port}")
|
||||
time.sleep(2) # 等待连接稳定
|
||||
|
||||
if "connected" not in connect_result.lower():
|
||||
print(f"❌ 无线连接失败: {connect_result}")
|
||||
continue
|
||||
else:
|
||||
print(f"✅ 无线连接成功: {device_ip}:{target_port}")
|
||||
else:
|
||||
print(f"✅ 已存在目标设备的有效连接,跳过新建")
|
||||
|
||||
# 验证连接
|
||||
wireless_id = f"{device_ip}:{target_port}"
|
||||
verify_result = run_command("adb devices")
|
||||
if wireless_id in verify_result and "device" in verify_result.split(wireless_id)[1][:10]:
|
||||
print(f"✅ 连接验证通过: {wireless_id}")
|
||||
else:
|
||||
print(f"❌ 连接验证失败,请检查")
|
||||
continue
|
||||
|
||||
# ===== 后续自动化 =====
|
||||
if not start_appium():
|
||||
print("❌ Appium启动失败,跳过后续操作")
|
||||
continue
|
||||
|
||||
# driver, app_started = start_settlement_app(wireless_id, device_ip, target_port)
|
||||
|
||||
# if not app_started:
|
||||
# print("⚠️ App启动失败,跳过后续操作")
|
||||
# continue
|
||||
|
||||
|
||||
print(f"🎉 所有操作完成! 设备 {serial} 已就绪")
|
||||
|
||||
# # 关闭Appium连接
|
||||
# if driver:
|
||||
# print("🔄 关闭Appium连接...")
|
||||
# driver.quit()
|
||||
|
||||
break # 处理完第一个设备后退出,如需处理多个设备可移除此行
|
||||
|
||||
|
||||
# =======================
|
||||
# 程序入口
|
||||
# =======================
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 配置参数
|
||||
setup_adb_wireless()
|
||||
1195
globals/driver_utils.py
Normal file
1195
globals/driver_utils.py
Normal file
File diff suppressed because it is too large
Load Diff
298
globals/ex_apis.py
Normal file
298
globals/ex_apis.py
Normal file
@@ -0,0 +1,298 @@
|
||||
# external_apis.py
|
||||
import requests
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
from typing import Dict, Tuple, Optional
|
||||
import time
|
||||
|
||||
class WeatherAPI:
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
# 使用腾讯天气API
|
||||
self.base_url = "https://wis.qq.com/weather/common"
|
||||
|
||||
def parse_city(self, city_string: str) -> Tuple[str, str, str]:
|
||||
"""
|
||||
解析城市字符串,返回省份、城市、区县
|
||||
|
||||
参数:
|
||||
city_string: 完整的地址字符串
|
||||
|
||||
返回:
|
||||
(province, city, county)
|
||||
"""
|
||||
# 匹配省份或自治区
|
||||
province_regex = r"(.*?)(省|自治区)"
|
||||
# 匹配城市或州
|
||||
city_regex = r"(.*?省|.*?自治区)(.*?市|.*?州)"
|
||||
# 匹配区、县或镇
|
||||
county_regex = r"(.*?市|.*?州)(.*?)(区|县|镇)"
|
||||
|
||||
province = ""
|
||||
city = ""
|
||||
county = ""
|
||||
|
||||
import re
|
||||
|
||||
# 先尝试匹配省份或自治区
|
||||
province_match = re.search(province_regex, city_string)
|
||||
if province_match:
|
||||
province = province_match.group(1).strip()
|
||||
|
||||
# 然后尝试匹配城市或州
|
||||
city_match = re.search(city_regex, city_string)
|
||||
if city_match:
|
||||
city = city_match.group(2).strip()
|
||||
else:
|
||||
# 如果没有匹配到城市,则可能是直辖市或者直接是区/县
|
||||
city = city_string
|
||||
|
||||
# 最后尝试匹配区、县或镇
|
||||
county_match = re.search(county_regex, city_string)
|
||||
if county_match:
|
||||
county = county_match.group(2).strip()
|
||||
# 如果有区、县或镇,那么前面的城市部分需要重新解析
|
||||
if city_match:
|
||||
city = city_match.group(2).strip()
|
||||
|
||||
# 特殊情况处理,去除重复的省市名称
|
||||
if city and province and city.startswith(province):
|
||||
city = city.replace(province, "").strip()
|
||||
if county and city and county.startswith(city):
|
||||
county = county.replace(city, "").strip()
|
||||
|
||||
# 去除后缀
|
||||
city = city.rstrip('市州')
|
||||
if county:
|
||||
county = county.rstrip('区县镇')
|
||||
|
||||
# self.logger.info(f"解析结果 - 省份: {province}, 城市: {city}, 区县: {county}")
|
||||
return province, city, county
|
||||
|
||||
def get_weather_by_qq_api(self, province: str, city: str, county: str) -> Optional[Dict]:
|
||||
"""
|
||||
使用腾讯天气API获取天气信息
|
||||
|
||||
参数:
|
||||
province: 省份
|
||||
city: 城市
|
||||
county: 区县
|
||||
|
||||
返回:
|
||||
天气信息字典 or None
|
||||
"""
|
||||
try:
|
||||
params = {
|
||||
'source': 'pc',
|
||||
'weather_type': 'observe',
|
||||
'province': province,
|
||||
'city': city,
|
||||
'county': county
|
||||
}
|
||||
|
||||
response = requests.get(self.base_url, params=params, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
|
||||
if data.get('status') == 200:
|
||||
observe_data = data.get('data', {}).get('observe', {})
|
||||
|
||||
return {
|
||||
'weather': observe_data.get('weather', ''),
|
||||
'temperature': observe_data.get('degree', ''),
|
||||
'pressure': observe_data.get('pressure', '1013')
|
||||
}
|
||||
else:
|
||||
self.logger.error(f"腾讯天气API错误: {data.get('message')}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"腾讯天气API调用失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def normalize_weather_text(self, weather_text: str) -> str:
|
||||
"""
|
||||
将天气描述标准化为: 晴;阴;雨;雪;风;其他
|
||||
|
||||
参数:
|
||||
weather_text: 原始天气描述
|
||||
|
||||
返回:
|
||||
标准化后的天气文本
|
||||
"""
|
||||
if not weather_text:
|
||||
return '其他'
|
||||
|
||||
weather_text_lower = weather_text.lower()
|
||||
|
||||
# 晴
|
||||
if any(word in weather_text_lower for word in ['晴', 'sunny', 'clear']):
|
||||
return '晴'
|
||||
# 阴
|
||||
elif any(word in weather_text_lower for word in ['阴', '多云', 'cloudy', 'overcast']):
|
||||
return '阴'
|
||||
# 雨
|
||||
elif any(word in weather_text_lower for word in ['雨', 'rain', 'drizzle', 'shower']):
|
||||
return '雨'
|
||||
# 雪
|
||||
elif any(word in weather_text_lower for word in ['雪', 'snow']):
|
||||
return '雪'
|
||||
# 风
|
||||
elif any(word in weather_text_lower for word in ['风', 'wind']):
|
||||
return '风'
|
||||
# 其他
|
||||
else:
|
||||
return '其他'
|
||||
|
||||
def adjust_pressure(self, pressure: float) -> float:
|
||||
"""
|
||||
调整气压值:低于700时,填700-750之间随机一个;高于700就按实际情况填
|
||||
|
||||
参数:
|
||||
pressure: 原始气压值
|
||||
|
||||
返回:
|
||||
调整后的气压值
|
||||
"""
|
||||
try:
|
||||
pressure_float = float(pressure)
|
||||
if pressure_float < 700:
|
||||
adjusted_pressure = random.randint(700, 750)
|
||||
self.logger.info(f"气压值 {pressure_float} 低于700,调整为: {adjusted_pressure:.1f}")
|
||||
return round(adjusted_pressure, 1)
|
||||
else:
|
||||
self.logger.info(f"使用实际气压值: {pressure_float}")
|
||||
return round(pressure_float, 1)
|
||||
except (ValueError, TypeError):
|
||||
self.logger.warning(f"气压值格式错误: {pressure},使用默认值720")
|
||||
return round(random.randint(700, 750), 1)
|
||||
|
||||
def get_weather_by_address(self, address: str, max_retries: int = 2) -> Optional[Dict]:
|
||||
"""
|
||||
根据地址获取天气信息
|
||||
|
||||
参数:
|
||||
address: 地址字符串
|
||||
max_retries: 最大重试次数
|
||||
|
||||
返回:
|
||||
{
|
||||
'weather': '晴/阴/雨/雪/风/其他',
|
||||
'temperature': 温度值,
|
||||
'pressure': 气压值
|
||||
} or None
|
||||
"""
|
||||
# self.logger.info(f"开始获取地址 '{address}' 的天气信息")
|
||||
|
||||
# 首先解析地址
|
||||
province, city, county = self.parse_city(address)
|
||||
|
||||
if not province and not city:
|
||||
self.logger.error("无法解析地址")
|
||||
return self.get_fallback_weather()
|
||||
|
||||
# 获取天气信息
|
||||
weather_data = None
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
# self.logger.info(f"尝试获取天气信息 (第{attempt + 1}次)")
|
||||
weather_data = self.get_weather_by_qq_api(province, city, county)
|
||||
if weather_data:
|
||||
break
|
||||
time.sleep(1) # 短暂延迟后重试
|
||||
except Exception as e:
|
||||
self.logger.warning(f"第{attempt + 1}次尝试失败: {str(e)}")
|
||||
time.sleep(1)
|
||||
|
||||
if not weather_data:
|
||||
self.logger.warning("获取天气信息失败,使用备用数据")
|
||||
return self.get_fallback_weather()
|
||||
|
||||
# 处理天气数据
|
||||
try:
|
||||
# 标准化天气文本
|
||||
normalized_weather = self.normalize_weather_text(weather_data['weather'])
|
||||
|
||||
# 调整气压值
|
||||
adjusted_pressure = self.adjust_pressure(weather_data['pressure'])
|
||||
|
||||
# 处理温度
|
||||
temperature = float(weather_data['temperature'])
|
||||
|
||||
result = {
|
||||
'weather': normalized_weather,
|
||||
'temperature': round(temperature, 1),
|
||||
'pressure': adjusted_pressure
|
||||
}
|
||||
|
||||
self.logger.info(f"成功获取天气信息: {result}")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"处理天气数据时出错: {str(e)}")
|
||||
return self.get_fallback_weather()
|
||||
|
||||
def get_fallback_weather(self) -> Dict:
|
||||
"""
|
||||
获取备用天气数据(当所有API都失败时使用)
|
||||
|
||||
返回:
|
||||
默认天气数据
|
||||
"""
|
||||
self.logger.info("使用备用天气数据")
|
||||
return {
|
||||
'weather': '阴',
|
||||
'temperature': round(random.randint(15, 30), 1),
|
||||
'pressure': round(random.randint(700, 750), 1)
|
||||
}
|
||||
|
||||
def get_weather_simple(self, address: str) -> Tuple[str, float, float]:
|
||||
"""
|
||||
简化接口:直接返回天气、温度、气压
|
||||
|
||||
参数:
|
||||
address: 地址字符串
|
||||
|
||||
返回:
|
||||
(weather, temperature, pressure)
|
||||
"""
|
||||
weather_data = self.get_weather_by_address(address)
|
||||
if weather_data:
|
||||
return weather_data['weather'], weather_data['temperature'], weather_data['pressure']
|
||||
else:
|
||||
fallback = self.get_fallback_weather()
|
||||
return fallback['weather'], fallback['temperature'], fallback['pressure']
|
||||
|
||||
# 创建全局实例
|
||||
weather_api = WeatherAPI()
|
||||
|
||||
# 直接可用的函数
|
||||
def get_weather_by_address(address: str) -> Optional[Dict]:
|
||||
"""
|
||||
根据地址获取天气信息(直接调用函数)
|
||||
|
||||
参数:
|
||||
address: 地址字符串
|
||||
|
||||
返回:
|
||||
{
|
||||
'weather': '晴/阴/雨/雪/风/其他',
|
||||
'temperature': 温度值,
|
||||
'pressure': 气压值
|
||||
} or None
|
||||
"""
|
||||
return weather_api.get_weather_by_address(address)
|
||||
|
||||
def get_weather_simple(address: str) -> Tuple[str, float, float]:
|
||||
"""
|
||||
简化接口:直接返回天气、温度、气压
|
||||
|
||||
参数:
|
||||
address: 地址字符串
|
||||
|
||||
返回:
|
||||
(weather, temperature, pressure)
|
||||
"""
|
||||
return weather_api.get_weather_simple(address)
|
||||
17
globals/global_variable.py
Normal file
17
globals/global_variable.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# 全局变量
|
||||
GLOBAL_DEVICE_ID = "" # 设备ID
|
||||
GLOBAL_USERNAME = "czyuzongwen" # 用户名
|
||||
GLOBAL_CURRENT_PROJECT_NAME = "" # 当前测试项目名称
|
||||
GLOBAL_LINE_NUM = "" # 线路编码
|
||||
GLOBAL_BREAKPOINT_STATUS_CODES = [0,3] # 要获取的断点状态码列表
|
||||
GLOBAL_UPLOAD_BREAKPOINT_LIST = []
|
||||
GLOBAL_UPLOAD_BREAKPOINT_DICT = {}
|
||||
GLOBAL_TESTED_BREAKPOINT_LIST = [] # 测量结束的断点列表
|
||||
LINE_TIME_MAPPING_DICT = {} # 存储所有线路编码和对应的时间的全局字典
|
||||
GLOBAL_BREAKPOINT_DICT = {} # 存储测量结束的断点名称和对应的线路编码的全局字典
|
||||
GLOBAL_NAME_TO_ID_MAP = {} # 存储所有数据员姓名和对应的身份证号的全局字典
|
||||
GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST = [] # 上传成功的`断点列表
|
||||
|
||||
|
||||
|
||||
|
||||
59
globals/ids.py
Normal file
59
globals/ids.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# ids.py
|
||||
# 登录界面
|
||||
LOGIN_USERNAME = "com.bjjw.cjgc:id/et_user_name"
|
||||
LOGIN_PASSWORD = "com.bjjw.cjgc:id/et_user_psw"
|
||||
LOGIN_BTN = "com.bjjw.cjgc:id/btn_login"
|
||||
|
||||
# 更新相关
|
||||
UPDATE_WORK_BASE = "com.bjjw.cjgc:id/btn_update_basepoint"
|
||||
UPDATE_LEVEL_LINE = "com.bjjw.cjgc:id/btn_update_line"
|
||||
UPDATE_LEVEL_LINE_CONFIRM = "com.bjjw.cjgc:id/commit"
|
||||
|
||||
# 弹窗 & 加载
|
||||
ALERT_DIALOG = "android:id/content"
|
||||
LOADING_DIALOG = "android:id/custom"
|
||||
|
||||
# 底部导航栏
|
||||
DOWNLOAD_TABBAR_ID = "com.bjjw.cjgc:id/img_1_layout"
|
||||
MEASURE_TABBAR_ID = "com.bjjw.cjgc:id/img_3_layout"
|
||||
|
||||
# 测量相关
|
||||
MEASURE_BTN_ID = "com.bjjw.cjgc:id/select_point_update_tip_tv"
|
||||
MEASURE_LIST_ID = "com.bjjw.cjgc:id/line_list"
|
||||
MEASURE_LISTVIEW_ID = "com.bjjw.cjgc:id/itemContainer"
|
||||
MEASURE_NAME_TEXT_ID = "com.bjjw.cjgc:id/title"
|
||||
MEASURE_NAME_ID = "com.bjjw.cjgc:id/sectName"
|
||||
MEASURE_BACK_ID = "com.bjjw.cjgc:id/btn_back"
|
||||
MEASURE_BACK_ID_2 = "com.bjjw.cjgc:id/stop_measure_btn"
|
||||
|
||||
# 天气、观测类型
|
||||
MEASURE_TITLE_ID = "com.bjjw.cjgc:id/title_bar"
|
||||
MEASURE_WEATHER_ID = "com.bjjw.cjgc:id/point_list_weather_sp"
|
||||
MEASURE_TYPE_ID = "com.bjjw.cjgc:id/point_list_mtype_sp"
|
||||
MEASURE_SELECT_ID = "android:id/select_dialog_listview"
|
||||
SELECT_DIALOG_TEXT1_ID = "android:id/text1"
|
||||
MEASURE_PRESSURE_ID = "com.bjjw.cjgc:id/point_list_barometric_et"
|
||||
MEASURE_TEMPERATURE_ID = "com.bjjw.cjgc:id/point_list_temperature_et"
|
||||
MEASURE_SAVE_ID = "com.bjjw.cjgc:id/select_point_order_save_btn"
|
||||
|
||||
# 日期选择器
|
||||
DATE_START = "com.bjjw.cjgc:id/date"
|
||||
DATE_END = "com.bjjw.cjgc:id/date_end"
|
||||
SCRCOLL_YEAR = "com.bjjw.cjgc:id/wheelView1"
|
||||
SCRCOLL_MONTH = "com.bjjw.cjgc:id/wheelView2"
|
||||
SCRCOLL_DAY = "com.bjjw.cjgc:id/wheelView3"
|
||||
SCRCOLL_CONFIRM = "com.bjjw.cjgc:id/okBtn"
|
||||
SCRCOLL_CANCEL = "com.bjjw.cjgc:id/cancelBtn"
|
||||
|
||||
# 其他
|
||||
CONNECT_LEVEL_METER = "com.bjjw.cjgc:id/point_conn_level_btn"
|
||||
PINGCHAR_PROCESS = "com.bjjw.cjgc:id/point_measure_btn"
|
||||
LEVEL_METER_MANAGER = "com.bjjw.cjgc:id/btn_back"
|
||||
LEVEL_METER_MANAGER_LIST = "com.bjjw.cjgc:id/total_station_expandlist"
|
||||
START_MEASURE = "com.bjjw.cjgc:id/btn_control_begin_or_end"
|
||||
ALTER_MEASURE = "com.bjjw.cjgc:id/order_title"
|
||||
MATCH_VISIABLE = "com.bjjw.cjgc:id/title_paired_devices"
|
||||
BLUETOOTH_PAIR = "com.bjjw.cjgc:id/paired_devices"
|
||||
REPID_MEASURE = "com.bjjw.cjgc:id/measure_remeasure_all_btn"
|
||||
ONE = "com.bjjw.cjgc:id/auto_measure_all_station_text"
|
||||
|
||||
Reference in New Issue
Block a user