From 9ce1dbadc1fcd44061c38a968ed4c2855c642620 Mon Sep 17 00:00:00 2001 From: YiLin <482244139@qq.com> Date: Wed, 4 Feb 2026 10:58:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E8=AE=BE=E5=A4=87=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=EF=BC=8C=E7=AB=AF=E5=8F=A3=E5=9B=BA=E5=AE=9A=EF=BC=8C=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=8E=A5=E5=8F=A3=E6=9C=AA=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/main.cpython-312.pyc | Bin 0 -> 19519 bytes __pycache__/permissions.cpython-312.pyc | Bin 11844 -> 11552 bytes create_a_link.py | 315 ++++++++++++ globals/__pycache__/apis.cpython-312.pyc | Bin 19714 -> 21363 bytes .../__pycache__/driver_utils.cpython-312.pyc | Bin 31423 -> 32278 bytes .../global_variable.cpython-312.pyc | Bin 611 -> 6544 bytes globals/apis.py | 50 +- globals/driver_utils.py | 26 +- globals/global_variable.py | 155 +++++- main.py | 204 ++------ main_init.py | 478 ++++++++++++++++++ .../download_tabbar_page.cpython-312.pyc | Bin 16553 -> 16618 bytes .../__pycache__/login_page.cpython-312.pyc | Bin 4007 -> 4686 bytes .../screenshot_page.cpython-312.pyc | Bin 68095 -> 67772 bytes .../upload_config_page.cpython-312.pyc | Bin 77778 -> 77842 bytes page_objects/download_tabbar_page.py | 12 +- page_objects/login_page.py | 30 +- page_objects/screenshot_page.py | 53 +- page_objects/upload_config_page.py | 8 +- permissions.py | 196 ++++--- scheduler.py | 100 ++++ test_results/上传失败的断点.txt | 14 +- 22 files changed, 1344 insertions(+), 297 deletions(-) create mode 100644 __pycache__/main.cpython-312.pyc create mode 100644 create_a_link.py create mode 100644 main_init.py create mode 100644 scheduler.py diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2240957b391378134c670c7c3533996805947cca GIT binary patch literal 19519 zcmd^nYjhJwmT7kW7{nvIpD}g-Lofu)OD0idOBe|~oR);KV>1j% z29qHJ>?8p;u$bMNvp=?d+*Mt5>(;H;y|-@N(x1}PQz>}Xee_dD{}PJ&Py7%bMKp4Ukx1nM>(bnN=h%ccr@1%xNT^;!?ZQ z%^4)FaAmqRW{o?`oJGEASGHSg)<)wxcaAwH8qYQ7k~;I;db6ISDP8&Q0&{`8&|K&) zG8egv&Bg8#a|y{)xeRWj*+}B4u2OfIxs1fqT;=Wxa|Ma3T@&0B%@avH-BszHWS&G* z63R|H*9n~w4?55cr zG#|Fp?bI&m7OI`D${)dM8>(nd)oyRLF^-*f20pYK95n&I*KPHCnHg)^{0^_jcZTMa zHEnHt)!xh;*z5rtvQhjy?NVG5kkH_I@i)CB++g41gtI>(yw~L+v@PFwYJ*fBfHMq?s0jo zwnl4nvz3XaW~^^v>`?u7uRofi+0ceiwO-FQN2`#w+RNC5@|e~+X~HC{uf-4TuJReD z7$;0}*>~Dq9xHy&hflZN=fiq^ojK9ATKUd~ZZ^5RtyNM^PKt0T+q?`60;k+=_1Ii? z#y4i>MW-=!2Uo-q%1$A}0A``KNnW6VSx8@?%rct{-g289-YK>ecq?oQcq?s6c<G zc&FM@;hknz+tO`mFVL~6oZdlKWpJ80`%XuTJvtwN#D*5&Rj~&-7!q=)h;Xnjx{I>J zGW`OHlv5TNV0Sire%0+kxCl)a9 z;REnU#d!SbLOGi(jvZ*o6w>3N(1kSR*9fW3Y#|m8ojLKi5c{4Ic>-KK{GJl|aY!NN zFA!ovON7uSe=9n=Fpe1lls`*|IZK3CJcK?dihl_)Tgq95fD5=CRi$kBD-P)GcRq@| zvCkOLLfklf;`xzdhlYRAGxUDXaL=L0ODD&tN8Y$_`?tSx_@MtdwcqaZTNt}9;PU$# zVdX0AyBv_>-BG3CR5mhGEe;!()@Eni4xbMh7pHG!tezNO5$7miwE-7s|~?yo}q63*0qe+35?UXwZ+-mVhIoqv(;^NcuZ{_ z9AG76SUU3)*P%(snurK5z-tdRm|xZ-JCJcVg;M8*)20T~rkwF@;!}&+vKj1*$JzX4*VN00AZyM3HAh=}ZKpk_JnV#7?5q`RQGH0g5-_PI z^frc+kBmYrJ*?CRmHLpfV5qS84=9$b(lFN#Vip!Z7GyhdN zF!#xUN$c3g=7GYN9$84KzoAS$n6^LdP+eG85!6-m`Ts*X^9~SAwM0cX#wiv26Lz8p zhH(Tw{jpq_W@5%ez$r?YxLY9)rfduX;xXpqN37&=-j4yq$_bDZmB>iq3;-xe&dyTe zrG=QV1c06xJBu%;hrx88_D5R@_+zc~$Jol>6n~n~CVz~j{7v!47_7fosOfKtKgOtk z^n4gA5K13Rr7$x=ki|ykA?Ke=EPy%`B0Ob6-SLnJvgGqvF2>`ZAe6ap?n9l5`MU;W!ocKI|vJ`){0Nq6kJqGHlNW|`?Y>IArH!V_Il>waLk)9Kg z(?5?Kc#T&}N6uV`ywt}OLQVkXkuh><59SL{Bd2~ha^fE&=Pus9a4LZPXB^(YXZZMj zae53m^3DN}M*#x?`}O{jV=v!6y?6L`KZmL!@BNw=RgpvIpu%Wf@4s^EmA=TqbGM)W zaQOI}!*Bo7tyAwr0rZ5J7#W02UZ~ACHdN&@xUPs);8LQ}M%E5GHG}huK|&c+BDho# zRt}G)4OB-4T7OELsrZ1+t@~J3oxolpzpn?wiE1#hw8E(*FEirVY z3KafKIlge+aH_ZhFSj#{m*J#7KXXRHOu<~*9+hZ}2}@MKtFvgFz!C}6c>v+_0B_$z zjmmOW*+UcN^vf=1T+A4luso

!~}i>ZtB`K1!xUq1C|VuL-Hw-YBW;lbuUDlXkxG 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 Ihp^HA0()jfVgLXD delta 3265 zcmaJ@X;2jD8UDVRp1X$`oSOk@0hK|;i|np=uP0utD_XUz1}AibgIxCX;KDXFtj9Vi z2CVjAJ=QVN4W$OL*i=}LXsYsKll>VkU;fJ5-Gh$%BVE<+`+e{6 zyvO%`Jrk|`)9#GFn$1Q84~0Ju&_$;*-1yL`**Sy?F<7`_u&aQbayw3aZVhW_4Wq3g zXzdZAQb!YvhStF=$+WZ{WO`X<0GTl**V87D8|c}x#>{9_))_}Ml}1^nVN8H%0V|R* z)7Jfk2=!q0=X54LtyWc;0a>8B=~8+K|Oa^VEZ8z;V)XKin%2@02gzZk7uo4?x z*1CdpYh_(GTiA=e*e6?cW2?o%IM$*)lL%Eh8K*4B z)RVdelKX5A%}Hqwcm>IgzSno2u}TX8L-RlmaRD&T71YMN*#;MD|ByOvi$T3;j7uA^}P z(VB}YUd!v20VtO`UI~rYO*JYwlhc4weuP@LJxptP1fA0HI-vSXnyJ7T(O80#DyIz+ zVAnF*_^t$!fWd7y@M=h6AtYhsHMFVC3@Z(9NUaQYM=^|Ct)Yo zCXbeVYRAhm7I)?FW?_=D+u?}bT!^mu1mex?hr%BBbZaV8J5NFy4%#VDX1fZU8m&%A zFVxR48!!tntdr9SyEWOuf3VSwVFr1E*GON59Y5*W)R}wIi*GghvO<4{tc?^rqeFM7 z)y>U;Py@9yusXywHTbzEb`h1lcrtPPO7fSH(L0gkrNa|n^~mO>&3@M3K+#M~pqi-) z(52LP{~r=pk5Of7)AcNKAQWJkaFxHlzLZM#-b(a*GycsN$=ipLogK-GoeJW_;rq!* zPvZDxC5!^VGF*sl1SdIYDghZlv3+(gl8knIhk4BDlu?r{MH#N zlp$l?yaJJxrc)szC1RJuB9sjT;70FW8yz|UDUY2$k~lgzak^g#K*^{S99|g4Cvo-m z__x_(8)yiImIy~rIQ|785kub0ECn0qRGKCV|RZub{9q? zk~TEUE7%YN(-PXI6qDW?X!cm&38h1TlDK$Yz1Y zXJMt!HoH`^%53&m@Aua-!78q)s+RRPawGP%!Fek+2Se4>OfXng-4p^N3)cj5R^{io zFw@Y?!Ms^gNL4Ho4ApZ%wn)N%BTc!#2`ITLp6nbc8%naaRkHyuPz}Iua~R2dBdqZU z>O%l87Z{Yr3O+A{^oRH#&+Ciw8Ev$d&)JIO0~x-kR5Aj$3o13&SyM!70n64NzaXk-^$cc9J~@ zds}<=_qhgK0|s%)w(rB@wkok|ujpsRy+P5z{h$v`E|hA1^&ZlY$F_X2rRV+LCB1XR znWbXp(qUrR6T&1*S4LNgd5Z^DJe>D1SNy0#%-lIl?1G+o*I|)x#obvV5q3QF6h8J8 z$2`T4JPTeRwPsPLIil;a$4!wMpz2=y0mN>igk!@+* zI^(gmFlH_6bNyglIH?2Q=SHOWJkeXaEuEI$*->BAE6&|0mhBcd?h*IYiwy@JH?+hW zTHtTvXShuD5~12gyh#Py2E18fo-L|Z2pP7jQW&C}8P?Z4*CM^`AEs>SAh&jI6|+7W zCM)AMcifU4wZts*Mg9C&+IN~K`VNYY&xZA_af=P2AT)SAcNXD(jf*Fw|6C)w`alC* zHJYt}9lKNbcm6uOYj8n9J0?do!RGn^$F2d5aA)RvyhFgVHftEzULV{(>!1p+68>Dc z%DO}H+zzBoP0gd&3c=%R!MlVWUyU`FwMk0(f|tNbFcuY)(x8tJbfB7J8DVWv39b@a zi@dm6I8)?Xz?Mr6c|cz4mo+rep?YQodl0tLhdj6puEwMqWBf0a`6J5s5t)SNMf*yA vfj`FB-lv{K5+5p1`1d6*<^AM7)dIYvFMkpN8z^`wvB{dj&N%`mq-g&GS~eUc diff --git a/create_a_link.py b/create_a_link.py new file mode 100644 index 0000000..5bff38a --- /dev/null +++ b/create_a_link.py @@ -0,0 +1,315 @@ +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 + + +# ======================= +# 基础工具函数 +# ======================= + +def run_command(command): + """执行系统命令并返回输出""" + result = subprocess.run(command, shell=True, capture_output=True, text=True) + return result.stdout.strip() + + +# ======================= +# API请求函数 +# ======================= + +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 [] + + +def update_device_info(account_id, device_name, device_port, device_ip): + """更新设备信息到服务器""" + url = "http://www.yuxindazhineng.com:3002/api/accounts/update" + headers = { + "Content-Type": "application/json" + } + data = { + "account_id": str(account_id), + "account_data": { + "device_name": device_name, + "device_port": device_port, + "device_ip": device_ip + } + } + + try: + print(f"🔄 更新设备信息,账户ID: {account_id}") + print(f" 设备信息: 名称={device_name}, 端口={device_port}, IP={device_ip}") + + 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('message', '未知信息')}") + return True + else: + print(f"❌ 更新失败: {result.get('message', '未知错误')}") + return False + else: + print(f"❌ 服务器响应错误: {response.status_code}") + return False + except requests.exceptions.RequestException as e: + print(f"❌ 网络请求失败: {e}") + return False + + +# ======================= +# Appium 启动 +# ======================= + +def start_appium(): + print("🚀 启动 Appium Server ...") + subprocess.Popen( + ["appium.cmd", "-a", "127.0.0.1", "-p", "4723"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + time.sleep(5) # 给 Appium 启动时间 + print("✅ Appium Server 已启动") + + +# ======================= +# 启动沉降观测 App +# ======================= + +def start_settlement_app(device_id, device_ip, device_port): + print(f"📱 使用 Appium 连接设备: {device_id}") + + options = UiAutomator2Options() + options.platform_name = "Android" + options.device_name = device_id + options.udid = device_id + + # ⚠️ TODO:替换为你的真实信息 + 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 = 28800 + + # 超时增强(无线 ADB 必须) + options.set_capability("uiautomator2ServerLaunchTimeout", 60000) + options.set_capability("adbExecTimeout", 120000) + + driver = webdriver.Remote( + "http://127.0.0.1:4723", + options=options + ) + + # 使用ADB命令启动Activity + try: + adb_command = f"adb -s {device_id} shell am start -n com.bjjw.cjgc/.activity.LoginActivity" + result = subprocess.run(adb_command, shell=True, capture_output=True, text=True) + if result.returncode == 0: + time.sleep(1) # 等待Activity启动 + except Exception: + return False + + print("✅ 沉降观测 App 已成功启动") + + # ======================= + # 获取用户名文本框内容 + # ======================= + app_username = None + try: + print("🔍 尝试获取用户名文本框内容...") + + # 创建显式等待对象 + wait = WebDriverWait(driver, 15) # 最多等待15秒 + + # 等待用户名文本框可点击 + username_field = wait.until( + EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/et_user_name")) + ) + + # 获取文本框中的文本内容 + app_username = username_field.text + + # 如果文本框是空的,尝试使用get_attribute获取文本 + if not app_username: + app_username = username_field.get_attribute("text") + + # 如果还是空的,尝试获取其他属性 + if not app_username: + app_username = username_field.get_attribute("content-desc") or username_field.get_attribute("label") + + if app_username: + print(f"✅ 成功获取到用户名: {app_username}") + else: + print("⚠️ 用户名文本框为空") + + except Exception as e: + print(f"❌ 获取用户名失败: {e}") + + return driver, app_username + + +# ======================= +# 无线 ADB 建链主流程 +# ======================= + +def setup_adb_wireless(port="5555", yh_id="68c0dbfdb7cbcd616e7c5ab5"): + print(f"🚀 开始无线 ADB 建链(端口 {port})") + print(f"📋 用户ID: {yh_id}") + + # 从服务器获取账户信息 + accounts = get_accounts_from_server(yh_id) + if not accounts: + print("❌ 未从服务器获取到账户信息,终止流程") + return + + devices_output = run_command("adb devices") + lines = devices_output.splitlines()[1:] + + usb_devices = [] + + for line in lines: + if not line.strip(): + continue + + device_id = line.split()[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}") + + # 切 TCP 模式 + run_command(f"adb -s {serial} tcpip {port}") + time.sleep(2) + + # 无线连接 + connect_result = run_command(f"adb connect {device_ip}:{port}") + + if "connected" not in connect_result.lower(): + print(f"❌ 无线连接失败: {connect_result}") + continue + + wireless_id = f"{device_ip}:{port}" + print(f"✅ 无线 ADB 成功: {wireless_id}") + + # ===== 后续自动化 ===== + start_appium() + driver, app_username = start_settlement_app(wireless_id, device_ip, port) + + if not app_username: + print("⚠️ 未获取到App中的用户名,跳过服务器更新") + continue + + # 在账户列表中查找匹配的用户名 + matched_account = None + for account in accounts: + if account.get("username") == app_username: + matched_account = account + break + + if not matched_account: + print(f"❌ 未找到与用户名 '{app_username}' 匹配的账户") + continue + + print(f"✅ 找到匹配账户: {matched_account.get('cl_name')} ({matched_account.get('username')})") + print(f" account_id: {matched_account.get('account_id')}") + + # 更新设备信息到服务器 + device_name = serial # 使用设备序列号作为设备名称 + + # 构建更新数据 + update_data = { + "account_id": matched_account.get("account_id"), + "device_name": device_name, + "device_port": port, + "device_ip": device_ip + } + + success = update_device_info( + account_id=matched_account.get("account_id"), + device_name=device_name, + device_port=port, + device_ip=device_ip + ) + + if success: + print(f"🎉 所有操作完成! 账户 {matched_account.get('username')} 的设备信息已更新") + else: + print(f"⚠️ 设备信息更新失败,但无线连接和App启动已完成") + + # 关闭Appium连接 + if driver: + print("🔄 关闭Appium连接...") + driver.quit() + + break # 处理完第一个设备后退出,如需处理多个设备可移除此行 + + +# ======================= +# 程序入口 +# ======================= + +if __name__ == "__main__": + # 配置参数 + TARGET_PORT = "5555" + USER_ID = "68c0dbfdb7cbcd616e7c5ab5" # 替换为实际的用户ID + + setup_adb_wireless(TARGET_PORT, USER_ID) \ No newline at end of file diff --git a/globals/__pycache__/apis.cpython-312.pyc b/globals/__pycache__/apis.cpython-312.pyc index 3e0e4db94217301c991fd1792e1f78a1b5e45dad..6c951182e04b079bcf390285deffd76258b3c5b4 100644 GIT binary patch delta 2414 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 diff --git a/globals/__pycache__/driver_utils.cpython-312.pyc b/globals/__pycache__/driver_utils.cpython-312.pyc index 81dc29df37504cf6cf780adbfa495067cd3d161b..ec3527223fe4978a8fd8cde8cf62c3c920ce2d1d 100644 GIT binary patch delta 3989 zcmZu!Yfu~472egddV|DU!hpofAl?r{jEw}njrafoHYdn=k z-=1^sxp(iL$9Gq6{Db}B*KEQG&UYXdOglYpV^aAADf@EcK#$ksUA?> z!{jki`SP56hLlg~?p!zCCRHtaOzNoJX05eP=?>{0-RV=V6YK=DpSzElVDDJ#g(>}- zwW@KC@wsc((!*M5#yeb+)AOX-mD}l;A2?7)Eqn=!izLVAOZCehflV|gA%h;`OwPyG}xQV=P1(g-W)z(k$&Bq$97Ync0}c{(;M0= zTlHdi?-&Vr1yPha5)R6GB1mBp^oD$bteNnTpg%Y$t3}Bt5F+c^_Irgf$sY>JoG1~Q z2V;ny)!kKPkaa#`pWiFE{XT+uA{7W}0J7dk{QCsr4wDh$KttSw$4}qs+hCAcK{SBH zZnx81eYasJ$Vu=MkErN(^^`59Cd0w$+iIh3n7yrL64M@+X6}2(oIaB=+jn|fw65cF z$8Da=O^6z^ztA$-MbGScYEQJZ@v;=%y(c;pcspzOq3(A~SvO5Zi>9K}&flBLZkf|h z7@siCOw1mb9gbGr6V2-VgSqcRu$rDQ%@&?^oL(2*&=>9Z-RvJ;>>rNyjYReK(YMV( zSYKzRj~cSKe4%5^*3TFg_H@gbd~D~#J7)%G{j>K+D|_GWyEl5@z>U7aXzx%o(|^+# zSTqK{y3L{QS6>(zV=8^gaD4jP@QCNNEgF}VdCTmqZnrU)RGFY$vgurD+@+#?S0XoO z(Fv@xhm0L$nzWzPF^g7<&PEw z4t}3U0gv)0n7B#ya0;|*HvJv1jl5>u%R^0MB-OD;B0Wh**_=LbcCa!(5}JVgehV}a z=iBXvNjF@{{q*0-CF}`WkaDgdeo&CytC=r`ahdlCd&7gWX2>IYB#DrFs3Fy+Zo;W< zTA8|SV;bt9!F@^R`_)^)D3C5?HrVm;vbv>aCfcfMW=c!725TdTRZ z%vp%l>0r93%WJFRE>;;)-qPT*bC(*k0O#zytAPF|JzakaqIiOtWW<%g(FTiyy+8*n zP3*Ju16`G)^Nkt7`eCTaqjLc zXL@(O>SAqc9$eo_&eB@&HpJXVR;*shfwD)l&vwnGT3`)>vnH%X7xXPPiRWO^Bn zM1E7>703RRY8w02;nJ`Z@*0+Sp>oax||9pkmMc+5%*Fwp(BYzUWc($ zk+~)}t5-pl62Ij4dPW?YRoWyWm?I~SdiKY<7kW?_ z9fQn1glp|k*8V<-q6R?WP zWmjH_l7)3>%tXLOlvSS5gL+80S11#u&{!|Y!=e1h&vqSWQ%E){HUv9D4gekKonR&U zUhn$pB2XPEWHqBo9+mqmrMX(JD~v%l<2p*Jx1gki2m>Q02>S(ZSQ6YKWOYnZ=ypvX zZAI@^0EcNg*U4b{El$;Xypn&PUpfGo0i%|OYIHIU6Eb=)!cMRXEO6C-k=84d?F8~ z`7|7!x9U30+tqe%F$>e zY&i0+@CsMH8f84VVzGS$B_+1aC@FLh>l^yI->JqDBrf`2zlD8;S^{=8(gC?n8v{k` z6*?H$0Mz$%Acub!%)OEGfh-Lhq<sK0}L9{SCAYegT5{Qy8(_oCbkAnTN~&J73m zoo`3uq@4Xboh7#T2XK-qwy9xymEocPc^VA8 zFs%Ttv12I%rIS>aGSyhhhAz09ZYrK#>z;eU1kh{M*1k7vv+bt2jC{@p?B#+QD^`kKs!Gh6?K!lkh^WDVj0@ znuuCSj8~P1S7M=b&M_F0RUwf)7df>*o{h0E{;^Qrc8F3hPokoA!5YnWmQDD) SUq!tK3)wpQ$id1uocll2Sn0k1 delta 3453 zcmZu!3v5%@8NSCi*Nzj%35lH;?7T>TI4{DpAtWTu0|%0sS74=EaUA;s7yRnE*GYIS zL2U=Cx)$}&cG}ueP1>}o+N$cJY3kTI9wA*q5p2@B-J-7D*w%IuWulv=?xFqvxlY2+ zEaf}@`Okk||KmG1FZ_$V^*7?2Ehxwn;McokQh9dr4QGYLazWIaEman_R$AsSep!5t z;J6?V;kb0%y5F)(s1dYUQzrGQVv&%yzH8Gdg zFlT(+Hn-TJ#yK6d?55RiF_&=0+}g^yp+ha~qIU^Focd6aGZs-agUD(w^w?ELD8CJa zV)#=p6INNkh?nUFN5$K@h3rSp3h{Qni!CZV+qxPoF6DOD^fn4t8tp-^cx7F;Em$gE z?I;R5Ez<=Aqf!)~WW1sex>an6h ziELnRxYrX+|HyrcRCmEHequ=R)EL~c8&3sOg{rDax&d0cnVl(XAg9@dvY*uz1qZiv z2L{8veS5a_^o6(d(sD2{q?kOCIbhfhM%0L=QM!qp^Hf@!arRbr#naz`@$y@_H|RQ? zk(c$J3f8;Pt`(sVK(zu?;jfGDZskk~=EnZrei`Ws!>unaVILZ;5%Rn_H)$}!qQ%w$( zH?#+R;?=ftz-gZZnrIH}HTQFXfIhkR3`7uIDyVZVeb4JBZ!yW&NzSo8-w#MX%3@vC z;V=8D^`qtGxnza@jb&dG`*y$@bSHq3r>II&)gsBLOwmt<7?&Z*z#U4O4x!njTzf#) zOc&e6A~dNa56}uW;`ayZ(F7}lqPu7%LKVV7F4kZdD~&EgSdOp)p$-ABL;dWR{*`SQ z9PVDDNR8~5!;fVYEo|m6ZGtg>Q8s3&cx+B@Zo@X#M=LPJ=|c!{0K)+p5Q!cP^LnAJ z>_UAlOq)evbWY1p6!Lc0eCX(M}W^^!`ybje{Q9uKQWR83BVv)1$}SU;%W zUj0u?{^KChClQ`tpR9{a;MP36R6z+X=|W^Hu=`a2L)xB7%Sn0~6+BuzEFzu2!MsR1 ztz;76Xd=d|C|kW%+3JBLgS%#J;JUy}^bE|~Mg)Ck{aw;J3>~O{o2DdWxa`MoLw{(q zUo>*5{8&a-HI*I&m5~oCX&E&fO~quW0p5FjZ#HZqLH}Y zw!pgd5tw5vN<_xZ8iz`i6KROAhHJHUB8~#6HULh+Rc02^Bn}~*Coudp01$A|0|d-Y zE_Uh1wjL%PZ@}JDXSrNw?`xATuL^Ybt&^tKNquele+Dj+ZlnqG_V z(vj%l$N@Q=j3i`EDyH9oHTbE(>m=lsUWe{2<_%3ahk*lM6zuTS&GE8+EmUB0BZZoj z=R_$Rg*&@z3eo9?7>SJ>wKS>paaO&@RrzrtC?V~g(MVd$P&o|6kj`jEjwX+5lYuz!qM9o~dRlLKTJ`VmT`f+m7$_7j)(4t5 zyM@b(*94uy6^8{>R|*3yTiwD`ryDv~-S%Ln6pmN?@MzIzd8G$dfk&LFLl3^a; zS6DJpW#ykCv+Qi5j$CFxO{`tPdEI*;6;4Fpy@jVE(fKoE-{Rka<*!&~bd~ixAVmqQ-sIAMKKd~6 zJPrLJSoS=$ls{5SQ2IJ+8TVOve}?TGuirMG#rGxTGiZtFYe;FTBsH^?vu~4g?rlmX z5|QMC?-QFH_e!^6>IeE?#(zQ@j^I*2ph9RfJqitYhZFE5>Zbq}o;G&#Xqoj-p!&?h z?i~Gbs0&2D7@~iMmVSgCqNv5#&>7z2BhcH2y#|wDIbHHQ@v1roLxv@#(&zQhjujA- zj@v-VKaMERu9Mh(3W2{`FQLR=CVT@-nnYgWRmL-eqSq;2lHzfB)G7L(KhK}%wkJ}t rOkCbX?|}}=Qc%wV+!YC7lPA_#+lcetIQ)BbVg+eszdzBEgH!(tx?f}B diff --git a/globals/__pycache__/global_variable.cpython-312.pyc b/globals/__pycache__/global_variable.cpython-312.pyc index 6e5b65c30bdabcf365ac808ecaca232806beb6c2..218bd49d7ebe0a6829c67f10f6e85e5f2fb15eb2 100644 GIT binary patch literal 6544 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 Ud>|}3* bool: # # 存入全局字典:key=line_num,value=line_name # global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT[line_num] = line_name # 存入全局字典:key=line_name,value=line_num - global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT[line_name] = line_num + global_variable.get_upload_breakpoint_dict()[line_name] = line_num # 如果line_name不在列表中,则添加 - if line_name not in global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST: - global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST.append(line_name) + if line_name not in global_variable.get_upload_breakpoint_list(): + global_variable.get_upload_breakpoint_list().append(line_name) logging.info(f"找到status=3的线路信息:line_num={line_num}, line_name={line_name}") found_valid_data = True if found_valid_data: - logging.info(f"成功提取所有status=3的线路信息,当前全局字典数据:{global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT}") + logging.info(f"成功提取所有status=3的线路信息,当前全局字典数据:{global_variable.get_upload_breakpoint_dict()}") return True else: logging.warning("data列表中未找到任何status=3且字段完整的线路信息") @@ -476,4 +476,36 @@ def get_line_info_and_save_global(user_name: str) -> bool: return False except Exception as e: logging.error(f"调用get_name_all接口时发生未知异常:{str(e)}", exc_info=True) # exc_info=True打印异常堆栈,方便排查 - return False \ No newline at end of file + return False + +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 [] diff --git a/globals/driver_utils.py b/globals/driver_utils.py index 6954ad6..dd2bf18 100644 --- a/globals/driver_utils.py +++ b/globals/driver_utils.py @@ -60,6 +60,21 @@ def init_appium_driver(device_id, app_package="com.bjjw.cjgc", app_activity=".ac # 等待应用稳定 time.sleep(2) + # 设置屏幕永不休眠 + try: + # 使用ADB命令设置屏幕永不休眠 + screen_timeout_cmd = [ + "adb", "-s", device_id, + "shell", "settings", "put", "system", "screen_off_timeout", "86400000" + ] + timeout_result = subprocess.run(screen_timeout_cmd, capture_output=True, text=True, timeout=15) + if timeout_result.returncode == 0: + logging.info(f"设备 {device_id} 已成功设置屏幕永不休眠") + else: + logging.warning(f"设备 {device_id} 设置屏幕永不休眠失败: {timeout_result.stderr}") + except Exception as timeout_error: + logging.warning(f"设备 {device_id} 设置屏幕永不休眠时出错: {str(timeout_error)}") + logging.info(f"设备 {device_id} Appium驱动初始化完成") return driver, wait @@ -87,7 +102,7 @@ def check_session_valid(driver, device_id=None): bool: 会话有效返回True,否则返回False """ if device_id is None: - device_id = global_variable.GLOBAL_DEVICE_ID + device_id = global_variable.get_device_id() device_str = f"设备 {device_id} " if device_id else "" if not driver: @@ -158,7 +173,7 @@ def reconnect_driver(device_id, old_driver=None, app_package="com.bjjw.cjgc", ap """ # 使用传入的device_id或从全局变量获取 if not device_id: - device_id = global_variable.GLOBAL_DEVICE_ID + device_id = global_variable.get_device_id() # 修复device_id参数类型问题并使用全局设备ID作为备用 actual_device_id = device_id @@ -174,7 +189,7 @@ def reconnect_driver(device_id, old_driver=None, app_package="com.bjjw.cjgc", ap # 如果仍然没有有效的设备ID,使用全局变量 if not actual_device_id or (isinstance(actual_device_id, str) and ("session=" in actual_device_id or len(actual_device_id.strip()) == 0)): - actual_device_id = global_variable.GLOBAL_DEVICE_ID + actual_device_id = global_variable.get_device_id() logging.warning(f"无法获取有效设备ID,使用全局变量GLOBAL_DEVICE_ID: {actual_device_id}") device_id = actual_device_id # 使用修正后的设备ID @@ -395,7 +410,6 @@ def ensure_appium_server_running(port=4723): logging.error("Appium 服务启动超时!请检查 appium 命令是否在命令行可直接运行。") return False -# ... (保留 init_appium_driver, safe_quit_driver 等其他函数) ... def wait_for_appium_start(port, timeout=10): """ @@ -442,7 +456,7 @@ def safe_quit_driver(driver, device_id=None): device_id: 设备ID(可选) """ if device_id is None: - device_id = global_variable.GLOBAL_DEVICE_ID + device_id = global_variable.get_device_id() device_str = f"设备 {device_id} " if device_id else "" logging.info(f"{device_str}开始关闭驱动") @@ -504,7 +518,7 @@ def launch_app_manually(driver, device_id, package_name="com.bjjw.cjgc", activit """ try: if not device_id: - device_id = global_variable.GLOBAL_DEVICE_ID + device_id = global_variable.get_device_id() # 尝试从driver获取设备ID if driver and hasattr(driver, 'capabilities'): device_id = driver.capabilities.get('udid') diff --git a/globals/global_variable.py b/globals/global_variable.py index 86549ce..454beef 100644 --- a/globals/global_variable.py +++ b/globals/global_variable.py @@ -1,16 +1,147 @@ # 全局变量 -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 = [] # 上传成功的`断点列表 +import threading + +# 线程本地存储,为每个线程创建独立的变量副本 +thread_local = threading.local() + +# 共享数据的锁 +upload_breakpoint_lock = threading.RLock() +upload_success_lock = threading.RLock() +line_time_mapping_lock = threading.RLock() +breakpoint_dict_lock = threading.RLock() +name_to_id_map_lock = threading.RLock() + +# 线程安全的全局变量访问函数 +def get_device_id(): + """获取当前线程的设备ID""" + if not hasattr(thread_local, 'GLOBAL_DEVICE_ID'): + thread_local.GLOBAL_DEVICE_ID = "" + return thread_local.GLOBAL_DEVICE_ID + +def set_device_id(device_id): + """设置当前线程的设备ID""" + thread_local.GLOBAL_DEVICE_ID = device_id + +def get_username(): + """获取用户名(全局共享)""" + if not hasattr(thread_local, 'GLOBAL_USERNAME'): + thread_local.GLOBAL_USERNAME = "czyuzongwen" + return thread_local.GLOBAL_USERNAME + +def set_username(username): + """设置用户名""" + thread_local.GLOBAL_USERNAME = username + +def get_current_project_name(): + """获取当前测试项目名称""" + if not hasattr(thread_local, 'GLOBAL_CURRENT_PROJECT_NAME'): + thread_local.GLOBAL_CURRENT_PROJECT_NAME = "" + return thread_local.GLOBAL_CURRENT_PROJECT_NAME + +def set_current_project_name(project_name): + """设置当前测试项目名称""" + thread_local.GLOBAL_CURRENT_PROJECT_NAME = project_name + +def get_line_num(): + """获取线路编码""" + if not hasattr(thread_local, 'GLOBAL_LINE_NUM'): + thread_local.GLOBAL_LINE_NUM = "" + return thread_local.GLOBAL_LINE_NUM + +def set_line_num(line_num): + """设置线路编码""" + thread_local.GLOBAL_LINE_NUM = line_num + +def get_breakpoint_status_codes(): + """获取要获取的断点状态码列表""" + return [0, 3] # 固定值,不需要线程本地存储 + +def get_upload_breakpoint_list(): + """获取上传断点列表""" + if not hasattr(thread_local, 'GLOBAL_UPLOAD_BREAKPOINT_LIST'): + thread_local.GLOBAL_UPLOAD_BREAKPOINT_LIST = [] + return thread_local.GLOBAL_UPLOAD_BREAKPOINT_LIST + +def set_upload_breakpoint_list(breakpoint_list): + """设置上传断点列表""" + thread_local.GLOBAL_UPLOAD_BREAKPOINT_LIST = breakpoint_list + +def get_upload_breakpoint_dict(): + """获取上传断点字典""" + if not hasattr(thread_local, 'GLOBAL_UPLOAD_BREAKPOINT_DICT'): + thread_local.GLOBAL_UPLOAD_BREAKPOINT_DICT = {} + return thread_local.GLOBAL_UPLOAD_BREAKPOINT_DICT + +def set_upload_breakpoint_dict(breakpoint_dict): + """设置上传断点字典""" + thread_local.GLOBAL_UPLOAD_BREAKPOINT_DICT = breakpoint_dict + +def get_tested_breakpoint_list(): + """获取测量结束的断点列表""" + if not hasattr(thread_local, 'GLOBAL_TESTED_BREAKPOINT_LIST'): + thread_local.GLOBAL_TESTED_BREAKPOINT_LIST = [] + return thread_local.GLOBAL_TESTED_BREAKPOINT_LIST + +def set_tested_breakpoint_list(tested_list): + """设置测量结束的断点列表""" + thread_local.GLOBAL_TESTED_BREAKPOINT_LIST = tested_list + +def get_line_time_mapping_dict(): + """获取线路编码和对应的时间的字典""" + if not hasattr(thread_local, 'LINE_TIME_MAPPING_DICT'): + thread_local.LINE_TIME_MAPPING_DICT = {} + return thread_local.LINE_TIME_MAPPING_DICT + +def set_line_time_mapping_dict(mapping_dict): + """设置线路编码和对应的时间的字典""" + thread_local.LINE_TIME_MAPPING_DICT = mapping_dict + +def get_breakpoint_dict(): + """获取测量结束的断点名称和对应的线路编码的字典""" + if not hasattr(thread_local, 'GLOBAL_BREAKPOINT_DICT'): + thread_local.GLOBAL_BREAKPOINT_DICT = {} + return thread_local.GLOBAL_BREAKPOINT_DICT + +def set_breakpoint_dict(breakpoint_dict): + """设置测量结束的断点名称和对应的线路编码的字典""" + thread_local.GLOBAL_BREAKPOINT_DICT = breakpoint_dict + +def get_name_to_id_map(): + """获取数据员姓名和对应的身份证号的字典""" + if not hasattr(thread_local, 'GLOBAL_NAME_TO_ID_MAP'): + thread_local.GLOBAL_NAME_TO_ID_MAP = {} + return thread_local.GLOBAL_NAME_TO_ID_MAP + +def set_name_to_id_map(name_id_map): + """设置数据员姓名和对应的身份证号的字典""" + thread_local.GLOBAL_NAME_TO_ID_MAP = name_id_map + +def get_upload_success_breakpoint_list(): + """获取上传成功的断点列表""" + if not hasattr(thread_local, 'GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST'): + thread_local.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST = [] + return thread_local.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST + +def set_upload_success_breakpoint_list(success_list): + """设置上传成功的断点列表""" + thread_local.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST = success_list + +# 为了保持 ,保留原有的全局变量名称 +# 但这些将不再被直接使用,而是通过上面的函数访问 +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 = [] # 上传成功的`断点列表 + +GLOBAL_YU_ID = "68e764f0f9548871c22f93b8" # 宇恒一号ID diff --git a/main.py b/main.py index 803a05b..a3f8663 100644 --- a/main.py +++ b/main.py @@ -32,156 +32,11 @@ logging.basicConfig( ] ) -class DeviceAutomation: - @staticmethod - def get_device_id() -> str: +class DeviceAutomation(object): - # """ - # 获取设备ID,优先使用已连接设备,否则使用全局配置 - # """ - # try: - # # 检查已连接设备 - # result = subprocess.run( - # ["adb", "devices"], - # capture_output=True, - # text=True, - # timeout=10 - # ) - - # # 解析设备列表 - # for line in result.stdout.strip().split('\n')[1:]: - # if line.strip() and "device" in line and "offline" not in line: - # device_id = line.split('\t')[0] - # logging.info(f"使用已连接设备: {device_id}") - # global_variable.GLOBAL_DEVICE_ID = device_id - # return device_id - - # except Exception as e: - # logging.warning(f"设备检测失败: {e}") - - """ - 获取设备ID,优先使用无线连接设备,否则尝试开启无线调试,最后使用全局配置 - """ - try: - # 检查已连接设备 - result = subprocess.run( - ["adb", "devices"], - capture_output=True, - text=True, - timeout=10 - ) - - # 解析设备列表 - wireless_device_id = None - usb_device_id = None - - # 先查找无线连接的设备(IP:端口格式) - for line in result.stdout.strip().split('\n')[1:]: - if line.strip() and "device" in line and "offline" not in line: - current_device = line.split('\t')[0] - # 检查是否为IP:端口格式的无线连接 - if ":" in current_device and any(char.isdigit() for char in current_device): - wireless_device_id = current_device - logging.info(f"使用无线连接设备: {wireless_device_id}") - global_variable.GLOBAL_DEVICE_ID = wireless_device_id - return wireless_device_id - else: - # 记录第一个USB连接的设备 - if not usb_device_id: - usb_device_id = current_device - - # 如果没有找到无线连接的设备,尝试使用USB设备开启无线调试 - if not wireless_device_id and usb_device_id: - logging.info(f"未找到无线连接设备,尝试使用USB设备 {usb_device_id} 开启无线调试") - - # 尝试获取设备IP地址 - try: - import re - import time - - ip_result = subprocess.run( - ["adb", "-s", usb_device_id, "shell", "ip", "-f", "inet", "addr", "show", "wlan0"], - capture_output=True, - text=True, - timeout=10 - ) - - # 解析IP地址 - ip_output = ip_result.stdout - if "inet " in ip_output: - # 提取IP地址 - ip_match = re.search(r'inet\s+(\d+\.\d+\.\d+\.\d+)', ip_output) - if ip_match: - device_ip = ip_match.group(1) - logging.info(f"获取到设备IP地址: {device_ip}") - - # 开启无线调试 - tcpip_result = subprocess.run( - ["adb", "-s", usb_device_id, "tcpip", "5555"], - capture_output=True, - text=True, - timeout=10 - ) - - if "restarting in TCP mode port: 5555" in tcpip_result.stdout: - logging.info("无线调试已开启,端口: 5555") - - # 等待几秒钟让设备准备好 - time.sleep(3) - - # 连接到无线设备 - connect_result = subprocess.run( - ["adb", "connect", f"{device_ip}:5555"], - capture_output=True, - text=True, - timeout=10 - ) - - if "connected to" in connect_result.stdout: - logging.info(f"成功连接到无线设备: {device_ip}:5555") - global_variable.GLOBAL_DEVICE_ID = f"{device_ip}:5555" - return f"{device_ip}:5555" - else: - logging.warning(f"连接无线设备失败: {connect_result.stderr}") - logging.info(f"使用USB设备: {usb_device_id}") - global_variable.GLOBAL_DEVICE_ID = usb_device_id - return usb_device_id - else: - logging.warning(f"开启无线调试失败: {tcpip_result.stderr}") - logging.info(f"使用USB设备: {usb_device_id}") - global_variable.GLOBAL_DEVICE_ID = usb_device_id - return usb_device_id - else: - logging.warning("未找到设备IP地址") - logging.info(f"使用USB设备: {usb_device_id}") - global_variable.GLOBAL_DEVICE_ID = usb_device_id - return usb_device_id - else: - logging.warning("无法获取设备IP地址,可能设备未连接到WiFi") - logging.info(f"使用USB设备: {usb_device_id}") - global_variable.GLOBAL_DEVICE_ID = usb_device_id - return usb_device_id - except Exception as e: - logging.warning(f"开启无线调试时出错: {str(e)}") - logging.info(f"使用USB设备: {usb_device_id}") - global_variable.GLOBAL_DEVICE_ID = usb_device_id - return usb_device_id - - except Exception as e: - logging.warning(f"设备检测失败: {e}") - - - # 使用全局配置 - device_id = global_variable.GLOBAL_DEVICE_ID - logging.info(f"使用全局配置设备: {device_id}") - return device_id def __init__(self, device_id=None): - # 如果没有提供设备ID,则自动获取 - if device_id is None: - self.device_id = self.get_device_id() - else: - self.device_id = device_id + self.device_id = device_id # 初始化权限 if permissions.grant_appium_permissions(self.device_id): @@ -282,7 +137,7 @@ class DeviceAutomation: return False # 获取状态为3的线路。 - apis.get_line_info_and_save_global(user_name=global_variable.GLOBAL_USERNAME); + apis.get_line_info_and_save_global(user_name=global_variable.get_username()); # # 虚拟数据替代 # global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT = {'CDWZQ-2标-龙骨湾右线大桥-0-7号墩-平原': 'L156372', 'CDWZQ-2标-蓝家湾特大 桥-31-31-平原': 'L159206'} # global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST = list(global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.keys()) @@ -299,7 +154,7 @@ class DeviceAutomation: # 检查是否有需要上传的断点 - if not global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST: + if not global_variable.get_upload_breakpoint_list(): logging.info(f"设备 {self.device_id} 断点列表为空,无需执行上传操作") return False @@ -312,12 +167,12 @@ class DeviceAutomation: # 遍历断点列表,逐个执行上传操作 upload_success_count = 0 - for breakpoint_name in global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST: + for breakpoint_name in global_variable.get_upload_breakpoint_list(): try: logging.info(f"设备 {self.device_id} 开始处理断点 '{breakpoint_name}' 的上传") # 安全地获取断点信息 - line_num = global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.get(breakpoint_name) + line_num = global_variable.get_upload_breakpoint_dict().get(breakpoint_name) if line_num is None: logging.warning(f"设备 {self.device_id} 断点 '{breakpoint_name}' 在字典中未找到,跳过上传") continue @@ -335,25 +190,25 @@ class DeviceAutomation: except Exception as e: logging.error(f"设备 {self.device_id} 处理断点 '{breakpoint_name}' 时发生异常: {str(e)}") - logging.info(f"设备 {self.device_id} 上传配置管理执行完成,成功上传 {upload_success_count}/{len(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)} 个断点") + logging.info(f"设备 {self.device_id} 上传配置管理执行完成,成功上传 {upload_success_count}/{len(global_variable.get_upload_breakpoint_list())} 个断点") # 如果所有断点都上传成功,返回True;否则返回False - all_upload_success = upload_success_count == len(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST) + all_upload_success = upload_success_count == len(global_variable.get_upload_breakpoint_list()) if all_upload_success: logging.info(f"设备 {self.device_id} 所有断点上传成功") # 把上传成功的断点写入日志文件"上传成功的断点.txt" with open(os.path.join(self.results_dir, "上传成功的断点.txt"), "w", encoding='utf-8') as f: - for bp in global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST: + for bp in global_variable.get_upload_success_breakpoint_list(): f.write(f"{bp}\n") else: logging.warning(f"设备 {self.device_id} 部分断点上传失败") # 把上传成功的断点写入日志文件"上传成功的断点.txt" with open(os.path.join(self.results_dir, "上传成功的断点.txt"), "w", encoding='utf-8') as f: - for bp in global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST: + for bp in global_variable.get_upload_success_breakpoint_list(): f.write(f"{bp}\n") # 把上传失败的断点写入日志文件"上传失败的断点.txt" with open(os.path.join(self.results_dir, "上传失败的断点.txt"), "w", encoding='utf-8') as f: - for bp in set(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)-set(global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST): + for bp in set(global_variable.get_upload_breakpoint_list())-set(global_variable.get_upload_success_breakpoint_list()): f.write(f"{bp}\n") return all_upload_success @@ -457,13 +312,48 @@ class DeviceAutomation: """关闭驱动""" safe_quit_driver(getattr(self, 'driver', None), self.device_id) + @staticmethod + def start_upload(device_id=None, upload_time=None): + """ + 供其他页面或模块调用的静态方法 + 执行完整的自动化流程 + + 参数: + device_id: 可选的设备ID,如果为None则自动获取 + + 返回: + bool: 自动化流程执行结果(True/False) + """ + automation = None + try: + # 创建自动化实例 + automation = DeviceAutomation(device_id=device_id) + + # 执行自动化流程 + success = automation.run_automation() + + if success: + logging.info("自动化流程执行成功") + else: + logging.error("自动化流程执行失败") + + return success + + except Exception as e: + logging.error(f"自动化流程执行出错: {str(e)}") + return False + finally: + # 确保资源被清理 + if automation: + automation.quit() + # 主执行逻辑 if __name__ == "__main__": # 单个设备配置 - 现在DeviceAutomation会自动获取设备ID try: - automation = DeviceAutomation() + automation = DeviceAutomation(device_id="192.168.1.100:5556") success = automation.run_automation() if success: diff --git a/main_init.py b/main_init.py new file mode 100644 index 0000000..803a05b --- /dev/null +++ b/main_init.py @@ -0,0 +1,478 @@ +# actions.py 主自动化脚本 +import os +import logging +import time +import subprocess +from appium import webdriver +from appium.options.android import UiAutomator2Options +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 globals.ids as ids +import globals.global_variable as global_variable # 导入全局变量模块 +import permissions # 导入权限处理模块 +import globals.apis as apis +from globals.driver_utils import init_appium_driver, ensure_appium_server_running, safe_quit_driver, is_app_launched, launch_app_manually +from page_objects.login_page import LoginPage +from page_objects.download_tabbar_page import DownloadTabbarPage +from page_objects.screenshot_page import ScreenshotPage +from page_objects.upload_config_page import UploadConfigPage +from page_objects.more_download_page import MoreDownloadPage + + +# 配置日志 +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s: %(message)s", + handlers=[ + logging.FileHandler("appium_automation.log"), + logging.StreamHandler() + ] +) + +class DeviceAutomation: + @staticmethod + def get_device_id() -> str: + + # """ + # 获取设备ID,优先使用已连接设备,否则使用全局配置 + # """ + # try: + # # 检查已连接设备 + # result = subprocess.run( + # ["adb", "devices"], + # capture_output=True, + # text=True, + # timeout=10 + # ) + + # # 解析设备列表 + # for line in result.stdout.strip().split('\n')[1:]: + # if line.strip() and "device" in line and "offline" not in line: + # device_id = line.split('\t')[0] + # logging.info(f"使用已连接设备: {device_id}") + # global_variable.GLOBAL_DEVICE_ID = device_id + # return device_id + + # except Exception as e: + # logging.warning(f"设备检测失败: {e}") + + """ + 获取设备ID,优先使用无线连接设备,否则尝试开启无线调试,最后使用全局配置 + """ + try: + # 检查已连接设备 + result = subprocess.run( + ["adb", "devices"], + capture_output=True, + text=True, + timeout=10 + ) + + # 解析设备列表 + wireless_device_id = None + usb_device_id = None + + # 先查找无线连接的设备(IP:端口格式) + for line in result.stdout.strip().split('\n')[1:]: + if line.strip() and "device" in line and "offline" not in line: + current_device = line.split('\t')[0] + # 检查是否为IP:端口格式的无线连接 + if ":" in current_device and any(char.isdigit() for char in current_device): + wireless_device_id = current_device + logging.info(f"使用无线连接设备: {wireless_device_id}") + global_variable.GLOBAL_DEVICE_ID = wireless_device_id + return wireless_device_id + else: + # 记录第一个USB连接的设备 + if not usb_device_id: + usb_device_id = current_device + + # 如果没有找到无线连接的设备,尝试使用USB设备开启无线调试 + if not wireless_device_id and usb_device_id: + logging.info(f"未找到无线连接设备,尝试使用USB设备 {usb_device_id} 开启无线调试") + + # 尝试获取设备IP地址 + try: + import re + import time + + ip_result = subprocess.run( + ["adb", "-s", usb_device_id, "shell", "ip", "-f", "inet", "addr", "show", "wlan0"], + capture_output=True, + text=True, + timeout=10 + ) + + # 解析IP地址 + ip_output = ip_result.stdout + if "inet " in ip_output: + # 提取IP地址 + ip_match = re.search(r'inet\s+(\d+\.\d+\.\d+\.\d+)', ip_output) + if ip_match: + device_ip = ip_match.group(1) + logging.info(f"获取到设备IP地址: {device_ip}") + + # 开启无线调试 + tcpip_result = subprocess.run( + ["adb", "-s", usb_device_id, "tcpip", "5555"], + capture_output=True, + text=True, + timeout=10 + ) + + if "restarting in TCP mode port: 5555" in tcpip_result.stdout: + logging.info("无线调试已开启,端口: 5555") + + # 等待几秒钟让设备准备好 + time.sleep(3) + + # 连接到无线设备 + connect_result = subprocess.run( + ["adb", "connect", f"{device_ip}:5555"], + capture_output=True, + text=True, + timeout=10 + ) + + if "connected to" in connect_result.stdout: + logging.info(f"成功连接到无线设备: {device_ip}:5555") + global_variable.GLOBAL_DEVICE_ID = f"{device_ip}:5555" + return f"{device_ip}:5555" + else: + logging.warning(f"连接无线设备失败: {connect_result.stderr}") + logging.info(f"使用USB设备: {usb_device_id}") + global_variable.GLOBAL_DEVICE_ID = usb_device_id + return usb_device_id + else: + logging.warning(f"开启无线调试失败: {tcpip_result.stderr}") + logging.info(f"使用USB设备: {usb_device_id}") + global_variable.GLOBAL_DEVICE_ID = usb_device_id + return usb_device_id + else: + logging.warning("未找到设备IP地址") + logging.info(f"使用USB设备: {usb_device_id}") + global_variable.GLOBAL_DEVICE_ID = usb_device_id + return usb_device_id + else: + logging.warning("无法获取设备IP地址,可能设备未连接到WiFi") + logging.info(f"使用USB设备: {usb_device_id}") + global_variable.GLOBAL_DEVICE_ID = usb_device_id + return usb_device_id + except Exception as e: + logging.warning(f"开启无线调试时出错: {str(e)}") + logging.info(f"使用USB设备: {usb_device_id}") + global_variable.GLOBAL_DEVICE_ID = usb_device_id + return usb_device_id + + except Exception as e: + logging.warning(f"设备检测失败: {e}") + + + # 使用全局配置 + device_id = global_variable.GLOBAL_DEVICE_ID + logging.info(f"使用全局配置设备: {device_id}") + return device_id + + def __init__(self, device_id=None): + # 如果没有提供设备ID,则自动获取 + if device_id is None: + self.device_id = self.get_device_id() + else: + self.device_id = device_id + + # 初始化权限 + if permissions.grant_appium_permissions(self.device_id): + logging.info(f"设备 {self.device_id} 权限授予成功") + else: + logging.warning(f"设备 {self.device_id} 权限授予失败") + + # 确保Appium服务器正在运行 + ensure_appium_server_running(4723) + + # 初始化驱动 + self.init_driver() + # 先拼接,后创建测试结果目录 + self.results_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_results') + os.makedirs(self.results_dir, exist_ok=True) + + + def init_driver(self): + """初始化Appium驱动""" + try: + # 使用全局函数初始化驱动 + self.driver, self.wait = init_appium_driver(self.device_id) + # 初始化页面对象 + logging.info(f"设备 {self.device_id} 开始初始化页面对象") + self.login_page = LoginPage(self.driver, self.wait) + self.download_tabbar_page = DownloadTabbarPage(self.driver, self.wait, self.device_id) + self.screenshot_page = ScreenshotPage(self.driver, self.wait, self.device_id) + self.upload_config_page = UploadConfigPage(self.driver, self.wait, self.device_id) + self.more_download_page = MoreDownloadPage(self.driver, self.wait,self.device_id) + logging.info(f"设备 {self.device_id} 所有页面对象初始化完成") + + # 检查应用是否成功启动 + if is_app_launched(self.driver): + logging.info(f"设备 {self.device_id} 沉降观测App已成功启动") + else: + logging.warning(f"设备 {self.device_id} 应用可能未正确启动") + # 手动启动应用 + launch_app_manually(self.driver, self.device_id) + + except Exception as e: + logging.error(f"设备 {self.device_id} 初始化驱动失败: {str(e)}") + raise + + def is_app_launched(self): + """检查应用是否已启动""" + try: + return is_app_launched(self.driver) + except Exception as e: + logging.error(f"设备 {self.device_id} 检查应用启动状态时出错: {str(e)}") + return False + + def is_element_present(self, by, value): + """检查元素是否存在""" + try: + self.driver.find_element(by, value) + return True + except NoSuchElementException: + return False + + def handle_app_state(self): + """根据当前应用状态处理相应的操作""" + try: + login_btn_exists = self.login_page.is_login_page() + if not login_btn_exists: + logging.error(f"设备 {self.device_id} 未知应用状态,无法确定当前页面,跳转到登录页面") + if self.navigate_to_login_page(self.driver, self.device_id): + logging.info(f"设备 {self.device_id} 成功跳转到登录页面") + return self.handle_app_state() # 递归调用处理登录后的状态 + else: + logging.error(f"设备 {self.device_id} 跳转到登录页面失败") + return False + + # 处理登录页面状态 + logging.info(f"设备 {self.device_id} 检测到登录页面,执行登录操作") + max_retries = 1 + login_success = False + + for attempt in range(max_retries + 1): + if self.login_page.login(): + login_success = True + break + else: + if attempt < max_retries: + logging.warning(f"设备 {self.device_id} 登录失败,准备重试 ({attempt + 1}/{max_retries})") + time.sleep(2) # 等待2秒后重试 + else: + logging.error(f"设备 {self.device_id} 登录失败,已达到最大重试次数") + + if not login_success: + return False + + logging.info(f"设备 {self.device_id} 登录成功,继续执行更新操作") + time.sleep(1) + + # 执行更新操作 + if not self.download_tabbar_page.download_tabbar_page_manager(): + logging.error(f"设备 {self.device_id} 更新操作执行失败") + return False + + # 获取状态为3的线路。 + apis.get_line_info_and_save_global(user_name=global_variable.GLOBAL_USERNAME); + # # 虚拟数据替代 + # global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT = {'CDWZQ-2标-龙骨湾右线大桥-0-7号墩-平原': 'L156372', 'CDWZQ-2标-蓝家湾特大 桥-31-31-平原': 'L159206'} + # global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST = list(global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.keys()) + + # 点击测量导航栏按钮 + measure_page_btn = WebDriverWait(self.driver, 5).until( + EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/img_3_layout")) + ) + measure_page_btn.click() + + + # 处理平差 + self.screenshot_page.screenshot_page_manager(self.device_id) + + + # 检查是否有需要上传的断点 + if not global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST: + logging.info(f"设备 {self.device_id} 断点列表为空,无需执行上传操作") + return False + + # 点击上传导航栏按钮 + upload_page_btn = WebDriverWait(self.driver, 5).until( + EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/img_2_layout")) + ) + upload_page_btn.click() + + + # 遍历断点列表,逐个执行上传操作 + upload_success_count = 0 + for breakpoint_name in global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST: + try: + logging.info(f"设备 {self.device_id} 开始处理断点 '{breakpoint_name}' 的上传") + + # 安全地获取断点信息 + line_num = global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.get(breakpoint_name) + if line_num is None: + logging.warning(f"设备 {self.device_id} 断点 '{breakpoint_name}' 在字典中未找到,跳过上传") + continue + if not line_num: + logging.warning(f"设备 {self.device_id} 断点 '{breakpoint_name}' 未获取到line_num,跳过上传") + continue + + # 执行上传配置管理,传入当前断点名称 + if self.upload_config_page.upload_config_page_manager(self.results_dir, breakpoint_name, line_num): + logging.info(f"设备 {self.device_id} 断点 '{breakpoint_name}' 上传成功") + upload_success_count += 1 + else: + logging.error(f"设备 {self.device_id} 断点 '{breakpoint_name}' 上传失败") + + except Exception as e: + logging.error(f"设备 {self.device_id} 处理断点 '{breakpoint_name}' 时发生异常: {str(e)}") + + logging.info(f"设备 {self.device_id} 上传配置管理执行完成,成功上传 {upload_success_count}/{len(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)} 个断点") + + # 如果所有断点都上传成功,返回True;否则返回False + all_upload_success = upload_success_count == len(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST) + if all_upload_success: + logging.info(f"设备 {self.device_id} 所有断点上传成功") + # 把上传成功的断点写入日志文件"上传成功的断点.txt" + with open(os.path.join(self.results_dir, "上传成功的断点.txt"), "w", encoding='utf-8') as f: + for bp in global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST: + f.write(f"{bp}\n") + else: + logging.warning(f"设备 {self.device_id} 部分断点上传失败") + # 把上传成功的断点写入日志文件"上传成功的断点.txt" + with open(os.path.join(self.results_dir, "上传成功的断点.txt"), "w", encoding='utf-8') as f: + for bp in global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST: + f.write(f"{bp}\n") + # 把上传失败的断点写入日志文件"上传失败的断点.txt" + with open(os.path.join(self.results_dir, "上传失败的断点.txt"), "w", encoding='utf-8') as f: + for bp in set(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)-set(global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST): + f.write(f"{bp}\n") + + return all_upload_success + + except Exception as e: + logging.error(f"设备 {self.device_id} 处理应用状态时出错: {str(e)}") + return False + + + + def check_and_click_confirm_popup_appium(self): + """ + 适用于Appium的弹窗检测函数 + + Returns: + bool: 是否成功处理弹窗 + """ + try: + from appium.webdriver.common.appiumby import AppiumBy + + # 使用self.driver而不是参数 + if not hasattr(self, 'driver') or self.driver is None: + logging.warning("driver未初始化,无法检测弹窗") + return False + + # 检查弹窗消息 + message_elements = self.driver.find_elements(AppiumBy.XPATH, '//android.widget.TextView[@text="是否退出测量界面?"]') + + if message_elements: + logging.info("检测到退出测量界面弹窗") + + # 点击"是"按钮 + confirm_buttons = self.driver.find_elements(AppiumBy.XPATH, '//android.widget.Button[@text="是" and @resource-id="android:id/button1"]') + if confirm_buttons: + confirm_buttons[0].click() + logging.info("已点击'是'按钮") + time.sleep(1) + return True + else: + logging.warning("未找到'是'按钮") + return False + else: + return False + + except Exception as e: + logging.error(f"Appium检测弹窗时发生错误: {str(e)}") + return False + + + + 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 run_automation(self): + """运行自动化流程""" + try: + success = self.handle_app_state() + # success = self.test_handle_app_state() + if success: + logging.info(f"设备 {self.device_id} 自动化流程执行成功") + else: + logging.error(f"设备 {self.device_id} 自动化流程执行失败") + return success + except Exception as e: + logging.error(f"设备 {self.device_id} 自动化执行过程中发生错误: {str(e)}") + return False + finally: + self.quit() + + def quit(self): + """关闭驱动""" + safe_quit_driver(getattr(self, 'driver', None), self.device_id) + +# 主执行逻辑 +if __name__ == "__main__": + + # 单个设备配置 - 现在DeviceAutomation会自动获取设备ID + + try: + automation = DeviceAutomation() + success = automation.run_automation() + + if success: + logging.info(f"设备自动化流程执行成功") + else: + logging.error(f"设备自动化流程执行失败") + except Exception as e: + logging.error(f"设备执行出错: {str(e)}") + + + + \ No newline at end of file diff --git a/page_objects/__pycache__/download_tabbar_page.cpython-312.pyc b/page_objects/__pycache__/download_tabbar_page.cpython-312.pyc index 02ad58c18d6d3ba508c84abfc327fd5c8da50c8f..6c94d573fba87f45b279d34854d084f4ce814d81 100644 GIT binary patch delta 388 zcmZ44$oQ&}k@qw&FBbz4@J2Lds&3@nZ^877Y4SuK1Ev(l$p?AlCLgfmm@H?h&uBDR zz*=|mAs$(<$P7zEG!YRibdd>G_HgxYtP~mDCi7YA!$dYmSvxZF@usJi#HXZ|WhSS_ zXQoVEW7Ev&FxkLXR@mjDy31vCw+m|S7u*vr$R=LlNtzsMt0?SwQOEPLj`sy^p9?-2 z7nCxu2xLv}vsFP6Praa&c10k4@*!Il8)WrR@f+eY4|oLny*s_H^T=G}k(p7xr2e`( zHVb9!hn75fd9%Q|6v}pz!A8RWs;C@lv{eqgu1&_oFvPoBXk|+1tDhl{q)bY8X?R&vD^MX>=6@l!@ z2W?dlf@v3&(ys_)O#XpQP~T2W1l`OV!s0(B&zBXATEM=B|GI|9MGcP&>Yf)o)30P? zU&zV5oRN1SJ^zAS!4;muJHisbKd>@!O8zR=0tQ->qn3*}!$DnJAbCg|L>|`E1(HX^ ZIbD^Qk0@BU$}%67Wp!2Be8EnN2>|+he(eAN diff --git a/page_objects/__pycache__/login_page.cpython-312.pyc b/page_objects/__pycache__/login_page.cpython-312.pyc index 691de42a453648df36375730c5c20ca78b4aa6e5..9311982ddfd4445e37c2dbd83f7f8fe7c8f74933 100644 GIT binary patch delta 969 zcmYjQ%WD%s9G=PkPXu#MsW^oA3KQzWFi3+}{4$Exr^4 z4j60l)M6xKdnj(Fn7P?_GAuvprba2sgYcs1n=j=gHDpCAi~Cm0Qukm{Ly)DSHMoVY zesl|okYWLTz_cVz&J(IBiqvbebwlXZpf@lMNY%0fH<{Io^G}wgw zXeTmdO;IoL!>^cjr+M?7RBLI1Xq(|!ZnJFW3@?A7=a9KwU^nCQ9R7@)u#?meS+~Ub zMgYMCxD1zS&(aeh4U2l|Q%R+|N~gJj+PQ!VKE1?euaN31<+7;cc^%x?awii&78lSt zG|w0JG)#k{v)6_PEh~+>B^^#oY14Cw3^#7D{_fuH9$&^f8Zm^wv%jy$hd)~8VJ$wv z#)7=2q(XC=q9(#KijK6D8kyC_(XoSp!=WQ1$BvDU9X+6<({UxN>a37*)$&P0^Bth4dtR3s6aOeJ)vXpGUj11nrJ z_s4kY^B`{;f%qzS-2RgRLhU;5S>-)%yaQ|eApXt`1y#2u_2n6wv{fuyuUOY5@2ccolUnl}?y%y@{0O83 z#}}#McYYfb93Kqw(*vX`{LJb*I%W)b;Xr-B37)v5fCW71^cehUb#*`jPkZ+D8RB_c zje$7}6%gng#}F=2feJcTh6s1EK|7t>(H!LI7d#5uR~$TKT!yXMfNO*aro*6_I{ck3 z$Cm^ShVV6Ed1!#-N#-t4D+n(mz?MKTh(RAcbRM7B=9o sq96KjrOoedD)p(ug#9=CW!XKGl;`5g9`zuO+1hO zuJ&$n49A$HL8Kg~y9Dzwb#-@P=HtSw?h=c4^D(k1096$!PCm#V$p-RXmF(n?%);Eo zrOC;u#l>l*Ij)oK1g5i@fK+KuzAYdpAr54=FnnTSW;Oc6#mXxAiBFSN@^cvDWFf&C zM#ahfg8Gb_llKeitEhtXaDoUc5Fr90gn+~^4x8Nkl+v73yCTQQLPEOyj*N_wpBO+S GSP=jyBYOD& diff --git a/page_objects/__pycache__/screenshot_page.cpython-312.pyc b/page_objects/__pycache__/screenshot_page.cpython-312.pyc index 76bb38a27cb19b80435ef7224e2369caea26ee92..08362212fc0c678efffa8903d83a237f3e62e78f 100644 GIT binary patch delta 1912 zcmZ`(dr(wW7(eIUecWXi7GjrWciCliS9XP5Sd52&;EH8RhA3F{m_^vl7Uj{hYS;?e z^lqK7PX}i*(_T80Q`D`&nr5=c7`|X{ckwdC6qKnoHG+_45AEDV(rUVYeCPbW`#aw` z-*Owws z5vN=)YbaJoC8(eA+;t}A` zI~s9h1G!oZNqbB&@ao+AFdhV)mp&tpQ|3jL1;h3^2U`T^-JJ7o-g%GUtmB+@ytAIS zH$ZXTqbVym#|qw2&D&}s)>V;J^$~r;AmfEdo>A5jOL{tGkROcg2U+>Qm~M_GGsz%D zs=(xOOfJvdA}~c9Qxs)N*rI~{&ry5gpjDCsl_%toK3va+KKEk$42<@<6s8akP$Ar$ z4B?;5Kne&&M~mkA?TF>9=-6h$bFeXDqr5~9; zFz*fS?|8Z6ghyD`$SrH+m#t&(7f#ZhYV`3Ox1?H)KFM{f$na^|!jwuEI+Eg^b7vOn zqbcI+le$IUQKJZsn%oKkj%A48m}B8Q0#44f5!)}b5m$efqS8tC-;!N9n;x)Ah=0H? zQU_*BiGRRJiyl{8HBAo`mzQR&ET#E*Qu3cKl@ec+X2?9MR@A8IXliDSoF0@*YgFvO zSKUfg37(Q<;PD$}i32VFxUi9)IEb88VhmMDX=1I}%V*c(l9*W6x2|hh?e)|Le5=>C zw)mSx`L+cEW#n$Z{2xM0&9DE^dB@W+0gNgQyE@-(5%TM}{5n3ro=dMERv86VKBvm( zRfU48j8l~zR!3FKN36L+X|{1Gy$mOWEB|I3O6OEd*av?skW$YxusJ>XnM|Y)NKh9B5U1mi=$8=sc)P)0{ z%}j{)rf!NS+0bGTwLV0@fV_qfMXWKH1??BJ;0Z%A^j<80ku)V3$LgT=l3vVEXSyCe zm^~f0F$ZQCld&1zzSNMMzM1J@+GVxK8=G~};X$zfTCB6&c(Uog?J}r(KMQ&;sg(bU zHqU&0r)hRGGZk|q-iRKiYtcp=qC?bL()CD5B;gWLmJPFKvhvG%%B&}cvz$Kk%;^vJ zo_TNU*#pm>d3yi3ec|ul44-+bs~YzHo=5+NvtjV}KCFj5f81p>w2^M`UwOyWJNZW* z%>DBV;}u%$$0I4ar<6mY8A;IZs^AmsW7I~lTxpVv9XYBihwWFAJBKw^K~u_UN+at# z#t>?y+Np7b7T`_PBtqIpsMA*aLABtx)=LFPE$67^9ryCq`yz>!AxCB;aTaMkj=~94 zm^7oSa!8jE$t>e^Wkac!VN14PS;|?K@|HXLJe=kJt|cRy)b5Jy6@tdbX>1YuZHHOI z)qIRk?X1Ex?GIrRVr(gelC<5*?Mflh$|YL&t~ua8aBrlrDq>p^Oi202vX=?GVU*{_ggt~hac!9bp1?w*$(Z*PR0lHay2LJ#7 delta 2206 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 K98r6Vpy6Na<-qI! delta 1600 zcmYjRdu&rx81L8npkpoV+O->92W!`Du8hYRbCZ|5dvET=IvzvEWp+5qSPPd?e%1`}DzCvAGN5cQ=JlHME}Lidnr9s^?Kcza%i%7az4LY~{6}-%lL1VG>}?mvu`U!N5%*mc zN3evI4DI_DEcD9$Y(Cb5PB#M|PX@Qf(@7t0(Zx?>)JBrqX?%7aZ~ z#6r}EiFCqF2L33AB9eA}G1QPv*VjWad2qcowiyG;5Xwn#XohHJbX{h2XHE{)@!D!2 z#$g-r49nVjAf1YZ?j1J7z-s!*on!%S)9w+A2)DyOYhepODD2}QpJ(HDbs&#%+OTc` zb`?FO-x7#SC8SHtZm}>vNIKVE#20^3DGEV-^_UK~NtgK(aUneL6KI z5nN)lfq1n%5#EOt;Twstih~sO1qV37n;eiKu)#qmIMnk_cmRI2+XdD zAr*39Rai;|Gl(3P<-?6`0I;vdK_8nxVtn}8=X*=)@x`mF)1bEaFKm4~2KkWzvJa4L zWGs^QD5tKfRw|ag^NWf~yk5DmzC?PiuC}74L9VLsHmHye&a}5M^&X~vf$$rHNKD6? zWr~cC>e2R;r9&Is3e$904t#E}EjR3X3d*!bsaG#u)|dC{%g;`{q;FJT^}yVX^YS&{ z=4CNF>7K;!lt~hq`7S}SF>`tfdd?(CI%d9iPiJ^GK^n`-Q zW-xOxPZF5n6drjnS>UiEEASS}d}=xN6QH2q_jUaR9Yc#v=TS!Qe^-e*j7;0={EqF~TB* zr3m;4lvaf3slALWgn(x;dPuvF?LpWao}UG74&GMRO@$2mX4EF&bPfjhbhF!oEgdVD N%~cMmUrvRE{{zx@wBP^$ diff --git a/page_objects/download_tabbar_page.py b/page_objects/download_tabbar_page.py index e753889..f333dde 100644 --- a/page_objects/download_tabbar_page.py +++ b/page_objects/download_tabbar_page.py @@ -295,25 +295,25 @@ class DownloadTabbarPage: """执行基础更新操作""" try: # 执行基础更新流程 - self.logger.info(f"设备 {global_variable.GLOBAL_DEVICE_ID} 开始执行更新流程") + self.logger.info(f"设备 {global_variable.get_device_id()} 开始执行更新流程") # 点击下载标签栏 if not self.click_download_tabbar(): - self.logger.error(f"设备 {global_variable.GLOBAL_DEVICE_ID} 点击下载标签栏失败") + self.logger.error(f"设备 {global_variable.get_device_id()} 点击下载标签栏失败") return False # 更新工作基点 if not self.update_work_base(): - self.logger.error(f"设备 {global_variable.GLOBAL_DEVICE_ID} 更新工作基点失败") + self.logger.error(f"设备 {global_variable.get_device_id()} 更新工作基点失败") return False # 更新水准线路 if not self.update_level_line(): - self.logger.error(f"设备 {global_variable.GLOBAL_DEVICE_ID} 更新水准线路失败") + self.logger.error(f"设备 {global_variable.get_device_id()} 更新水准线路失败") return False - self.logger.info(f"设备 {global_variable.GLOBAL_DEVICE_ID} 更新操作执行成功") + self.logger.info(f"设备 {global_variable.get_device_id()} 更新操作执行成功") return True except Exception as e: - self.logger.error(f"设备 {global_variable.GLOBAL_DEVICE_ID} 执行更新操作时出错: {str(e)}") + self.logger.error(f"设备 {global_variable.get_device_id()} 执行更新操作时出错: {str(e)}") return False \ No newline at end of file diff --git a/page_objects/login_page.py b/page_objects/login_page.py index f02b3a2..036a970 100644 --- a/page_objects/login_page.py +++ b/page_objects/login_page.py @@ -35,14 +35,40 @@ class LoginPage: # 读取文本框内已有的用户名(.text属性获取元素显示的文本内容) existing_username = username_field.text - # 3. 将获取到的用户名写入全局变量中 - global_variable.GLOBAL_USERNAME = existing_username # 关键:给全局变量赋值 + # 3. 将获取到的用户名写入全局变量中 + # global_variable.GLOBAL_USERNAME = existing_username # 关键:给全局变量赋值 + global_variable.set_username(existing_username) # 日志记录获取到的已有用户名(若为空,也需明确记录,避免后续误解) if existing_username.strip(): # 去除空格后判断是否有有效内容 self.logger.info(f"已获取文本框中的已有用户名: {existing_username}") else: self.logger.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 + + # 3. 输入密码 + if existing_username=="wangshun": + password_field.send_keys("Wang93534.") + else: + password_field.send_keys("Liang/1974.") + + # 4. 可选:隐藏键盘 + try: + self.driver.hide_keyboard() + except: + pass # 点击登录按钮 login_btn = self.wait.until( diff --git a/page_objects/screenshot_page.py b/page_objects/screenshot_page.py index 15e3a83..210078f 100644 --- a/page_objects/screenshot_page.py +++ b/page_objects/screenshot_page.py @@ -258,8 +258,8 @@ class ScreenshotPage: tuple: (date_str, time_str) 日期和时间字符串 """ - if line_code in global_variable.LINE_TIME_MAPPING_DICT: - end_time = global_variable.LINE_TIME_MAPPING_DICT[line_code] + if line_code in global_variable.get_line_time_mapping_dict(): + end_time = global_variable.get_line_time_mapping_dict()[line_code] date_str = end_time.strftime("%Y-%m-%d") time_str = end_time.strftime("%H:%M:%S") return (date_str, time_str) @@ -273,19 +273,19 @@ class ScreenshotPage: """ self.logger.info("\n当前全局字典内容:") - if global_variable.LINE_TIME_MAPPING_DICT: - for line_code, end_time in sorted(global_variable.LINE_TIME_MAPPING_DICT.items()): + if global_variable.get_line_time_mapping_dict(): + for line_code, end_time in sorted(global_variable.get_line_time_mapping_dict().items()): date_str = end_time.strftime("%Y-%m-%d") time_str = end_time.strftime("%H:%M:%S") self.logger.info(f" {line_code}: {date_str} {time_str}") else: self.logger.info(" 全局字典为空") - self.logger.info(f"总计: {len(global_variable.LINE_TIME_MAPPING_DICT)} 条记录\n") + self.logger.info(f"总计: {len(global_variable.get_line_time_mapping_dict())} 条记录\n") def clear_line_time_mapping(self): """ 清空全局字典 """ - global_variable.LINE_TIME_MAPPING_DICT.clear() + global_variable.get_line_time_mapping_dict().clear() self.logger.info("已清空全局字典") def set_device_time(self, device_id, time_str=None, date_str=None, disable_auto_sync=True): @@ -1121,10 +1121,10 @@ class ScreenshotPage: # 确保device_id正确设置,使用全局变量作为备用 if not hasattr(self, 'device_id') or not self.device_id: # 优先使用传入的device_id,其次使用全局变量 - self.device_id = device_id if device_id else global_variable.GLOBAL_DEVICE_ID + self.device_id = device_id if device_id else global_variable.get_device_id() # 使用self.device_id,确保有默认值 - actual_device_id = self.device_id if self.device_id else global_variable.GLOBAL_DEVICE_ID + actual_device_id = self.device_id if self.device_id else global_variable.get_device_id() if not check_session_valid(self.driver, actual_device_id): self.logger.warning(f"设备 {actual_device_id} 会话无效,尝试重新连接驱动...") @@ -1269,9 +1269,9 @@ class ScreenshotPage: def add_breakpoint_to_upload_list(self, breakpoint_name, line_num): """添加平差完成的断点到上传列表和字典""" - if breakpoint_name and breakpoint_name not in global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST: - global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST.append(breakpoint_name) - global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT[breakpoint_name] = { + if breakpoint_name and breakpoint_name not in global_variable.get_upload_breakpoint_list(): + global_variable.get_upload_breakpoint_list().append(breakpoint_name) + global_variable.get_upload_breakpoint_dict()[breakpoint_name] = { 'breakpoint_name': breakpoint_name, 'line_num': line_num } @@ -1342,19 +1342,18 @@ class ScreenshotPage: return False # 检查GLOBAL_UPLOAD_BREAKPOINT_DICT是否为空,如果为空则初始化一些测试数据 - if not global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT: + if not global_variable.get_upload_breakpoint_dict(): self.logger.warning("global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT为空,正在初始化测试数据") - global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT = {'CDWZQ-2标-龙骨湾右线大桥-0-7号墩-平原': 'L156372', 'CDWZQ-2标-蓝家湾特大 桥-31-31-平原': 'L159206'} + global_variable.set_upload_breakpoint_dict({'CDWZQ-2标-龙骨湾右线大桥-0-7号墩-平原': 'L156372', 'CDWZQ-2标-蓝家湾特大 桥-31-31-平原': 'L159206'}) - # 创建断点列表的副本,用于重试时重新处理 - breakpoint_names = list(global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.keys()) + breakpoint_names = list(global_variable.get_upload_breakpoint_dict().keys()) processed_breakpoints = [] # 开始循环处理断点 for breakpoint_name in breakpoint_names: if breakpoint_name in processed_breakpoints: continue - line_code = global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT[breakpoint_name] + line_code = global_variable.get_upload_breakpoint_dict()[breakpoint_name] self.logger.info(f"开始处理要平差的断点 {breakpoint_name}") @@ -1409,17 +1408,19 @@ class ScreenshotPage: self.logger.error(f"设备 {device_id} 处理返回按钮确认失败") continue - # 成功处理完一个断点,添加到已处理列表 - processed_breakpoints.append(breakpoint_name) - self.logger.info(f"成功处理断点: {breakpoint_name}") + # # 成功处理完一个断点,添加到已处理列表 + # processed_breakpoints.append(breakpoint_name) + # self.logger.info(f"成功处理断点: {breakpoint_name}") - # 检查是否所有断点都处理完成 - if len(processed_breakpoints) == len(breakpoint_names): - self.logger.info(f"设备 {device_id} 平差页面操作执行完成") - return True - else: - self.logger.warning(f"设备 {device_id} 部分断点处理失败,已成功处理 {len(processed_breakpoints)}/{len(breakpoint_names)} 个断点") - return True + # # 检查是否所有断点都处理完成 + # if len(processed_breakpoints) == len(breakpoint_names): + # self.logger.info(f"设备 {device_id} 平差页面操作执行完成") + # return True + # else: + # self.logger.warning(f"设备 {device_id} 部分断点处理失败,已成功处理 {len(processed_breakpoints)}/{len(breakpoint_names)} 个断点") + # return True + self.logger.warning(f"设备 {device_id} 上传流程执行完成") + return True except Exception as e: retry_count += 1 diff --git a/page_objects/upload_config_page.py b/page_objects/upload_config_page.py index 1ac8ef0..9c17cb8 100644 --- a/page_objects/upload_config_page.py +++ b/page_objects/upload_config_page.py @@ -671,7 +671,7 @@ class UploadConfigPage: logging.warning(f"跳过无效数据: 姓名='{name}', 身份证='{id_card}'") # 将字典保存到全局变量 - global_variable.GLOBAL_NAME_TO_ID_MAP = name_id_map + global_variable.set_name_to_id_map(name_id_map) logging.info(f"成功加载用户数据,共 {len(df)} 条记录,{len(name_id_map)} 个有效姓名-身份证映射") @@ -711,7 +711,7 @@ class UploadConfigPage: logging.info(f"使用第一个数据员: {sjname}") # 获取身份证号码 - id_card = global_variable.GLOBAL_NAME_TO_ID_MAP.get(sjname) + id_card = global_variable.get_name_to_id_map().get(sjname) logging.info(f"id_card: {id_card}") if not id_card: logging.error(f"未找到数据员 {sjname} 对应的身份证号") @@ -1759,7 +1759,7 @@ class UploadConfigPage: else: self.logger.info("页面中包含变化量属性,继续执行后续操作") - user_id = global_variable.GLOBAL_USERNAME + user_id = global_variable.get_username() if user_id is None: self.logger.error("获取用户ID失败") return False @@ -1859,7 +1859,7 @@ class UploadConfigPage: self.logger.info("上传配置页面操作执行完成") # 把上传成功的断点写入全局变量GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST - global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST.append(breakpoint_name) + global_variable.get_upload_success_breakpoint_list().append(breakpoint_name) return True diff --git a/permissions.py b/permissions.py index 8fc9a94..eeb78f7 100644 --- a/permissions.py +++ b/permissions.py @@ -2,6 +2,7 @@ import subprocess import logging import time +import os # 配置日志 logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s: %(message)s") @@ -83,90 +84,143 @@ def grant_single_permission(device_id: str, package: str, permission: str) -> bo logging.error(f"设备 {device_id}:处理 {package} 时发生未知错误:{str(e)}") return False + +# def grant_appium_permissions(device_id: str, require_all: bool = False) -> bool: +# """ +# 为 Appium UiAutomator2 服务授予权限 +# :param device_id: 设备 ID +# :param require_all: 是否要求所有权限都成功授予 +# :return: 权限授予是否成功(根据require_all参数判断) +# """ +# # 首先检查设备连接 +# if not check_device_connection(device_id): +# return False + +# packages_to_grant = [ +# "io.appium.settings", +# "io.appium.uiautomator2.server", +# "io.appium.uiautomator2.server.test" +# ] + +# # 权限列表(按优先级排序) +# permissions_to_grant = [ +# "android.permission.SET_ANIMATION_SCALE", +# "android.permission.CHANGE_CONFIGURATION", +# "android.permission.WRITE_SETTINGS", +# "android.permission.DISABLE_KEYGUARD", +# ] + +# success_count = 0 +# total_attempted = 0 +# package_results = {} + +# # 检查并授予权限 +# for package in packages_to_grant: +# package_results[package] = {"installed": False, "permissions": {}} + +# if not is_package_installed(device_id, package): +# logging.warning(f"设备 {device_id}:包 {package} 未安装,跳过权限授予") +# package_results[package]["installed"] = False +# continue + +# package_results[package]["installed"] = True +# package_success = 0 +# package_attempted = 0 + +# for permission in permissions_to_grant: +# total_attempted += 1 +# package_attempted += 1 + +# result = grant_single_permission(device_id, package, permission) +# package_results[package]["permissions"][permission] = result + +# if result: +# success_count += 1 +# package_success += 1 + +# # 记录每个包的授权结果 +# logging.info(f"设备 {device_id}:包 {package} 权限授予结果: {package_success}/{package_attempted}") + +# # 统计和报告 +# logging.info(f"设备 {device_id}:权限授予完成") +# logging.info(f"总计: 尝试 {total_attempted} 次,成功 {success_count} 次") + +# # 检查每个包的关键权限 +# critical_permission = "android.permission.WRITE_SECURE_SETTINGS" +# critical_failures = [] + +# for package, info in package_results.items(): +# if info["installed"] and critical_permission in info["permissions"]: +# if not info["permissions"][critical_permission]: +# critical_failures.append(package) + +# if critical_failures: +# logging.warning(f"设备 {device_id}:以下包的关键权限 {critical_permission} 授予失败: {', '.join(critical_failures)}") +# logging.warning("这可能会影响某些自动化功能,但基础测试通常不受影响") + +# # 根据require_all参数返回结果 +# if require_all: +# # 要求所有权限都成功 +# if critical_failures: +# logging.error("关键权限授予失败,无法继续(require_all=True)") +# return False +# return success_count == total_attempted +# else: +# # 不要求所有权限,只要设备连接正常就返回True +# logging.info(f"设备 {device_id}:权限授予过程完成,建议重启设备或Appium服务使更改生效") +# return True + def grant_appium_permissions(device_id: str, require_all: bool = False) -> bool: """ - 为 Appium UiAutomator2 服务授予权限 - :param device_id: 设备 ID - :param require_all: 是否要求所有权限都成功授予 - :return: 权限授予是否成功(根据require_all参数判断) + 修复版:为 Appium 授予权限(使用正确的方法) """ - # 首先检查设备连接 - if not check_device_connection(device_id): - return False + logging.info(f"设备 {device_id}:开始设置Appium权限") - packages_to_grant = [ - "io.appium.settings", - "io.appium.uiautomator2.server", - "io.appium.uiautomator2.server.test" - ] - - # 权限列表(按优先级排序) - permissions_to_grant = [ - "android.permission.WRITE_SECURE_SETTINGS", - "android.permission.CHANGE_CONFIGURATION", - "android.permission.DUMP", + # 1. 使用系统设置命令(替代原来的pm grant尝试) + logging.info("使用系统设置命令...") + system_commands = [ + ["adb", "-s", device_id, "shell", "settings", "put", "global", "window_animation_scale", "0"], + ["adb", "-s", device_id, "shell", "settings", "put", "global", "transition_animation_scale", "0"], + ["adb", "-s", device_id, "shell", "settings", "put", "global", "animator_duration_scale", "0"], + ["adb", "-s", device_id, "shell", "settings", "put", "system", "screen_off_timeout", "86400000"], ] success_count = 0 - total_attempted = 0 - package_results = {} - - # 检查并授予权限 - for package in packages_to_grant: - package_results[package] = {"installed": False, "permissions": {}} - - if not is_package_installed(device_id, package): - logging.warning(f"设备 {device_id}:包 {package} 未安装,跳过权限授予") - package_results[package]["installed"] = False - continue - - package_results[package]["installed"] = True - package_success = 0 - package_attempted = 0 - - for permission in permissions_to_grant: - total_attempted += 1 - package_attempted += 1 - - result = grant_single_permission(device_id, package, permission) - package_results[package]["permissions"][permission] = result - - if result: + for cmd in system_commands: + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) + if result.returncode == 0: success_count += 1 - package_success += 1 - - # 记录每个包的授权结果 - logging.info(f"设备 {device_id}:包 {package} 权限授予结果: {package_success}/{package_attempted}") + logging.info(f" 成功: {' '.join(cmd[3:])}") + else: + logging.warning(f" 失败: {' '.join(cmd[3:])}") + except: + logging.warning(f" 异常: {' '.join(cmd[3:])}") - # 统计和报告 - logging.info(f"设备 {device_id}:权限授予完成") - logging.info(f"总计: 尝试 {total_attempted} 次,成功 {success_count} 次") + # 2. 授予可自动授予的权限 + logging.info("授予基础权限...") + grantable = [ + "android.permission.INTERNET", + "android.permission.ACCESS_NETWORK_STATE", + "android.permission.ACCESS_WIFI_STATE", + ] - # 检查每个包的关键权限 - critical_permission = "android.permission.WRITE_SECURE_SETTINGS" - critical_failures = [] + for perm in grantable: + cmd = ["adb", "-s", device_id, "shell", "pm", "grant", "io.appium.settings", perm] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) + if result.returncode == 0: + success_count += 1 + logging.info(f" 成功授予: {perm.split('.')[-1]}") + else: + logging.debug(f" 跳过: {perm.split('.')[-1]}") - for package, info in package_results.items(): - if info["installed"] and critical_permission in info["permissions"]: - if not info["permissions"][critical_permission]: - critical_failures.append(package) + # 3. 返回结果 + logging.info(f"设置完成,成功项数: {success_count}") - if critical_failures: - logging.warning(f"设备 {device_id}:以下包的关键权限 {critical_permission} 授予失败: {', '.join(critical_failures)}") - logging.warning("这可能会影响某些自动化功能,但基础测试通常不受影响") - - # 根据require_all参数返回结果 if require_all: - # 要求所有权限都成功 - if critical_failures: - logging.error("关键权限授予失败,无法继续(require_all=True)") - return False - return success_count == total_attempted + return success_count == (len(system_commands) + len(grantable)) else: - # 不要求所有权限,只要设备连接正常就返回True - logging.info(f"设备 {device_id}:权限授予过程完成,建议重启设备或Appium服务使更改生效") - return True - + return success_count > 0 # 只要有成功项就返回True def check_appium_compatibility(device_id: str) -> dict: """ 检查Appium兼容性 @@ -217,6 +271,8 @@ def check_appium_compatibility(device_id: str) -> dict: logging.error(f"检查兼容性时出错: {str(e)}") return {"device_id": device_id, "error": str(e)} + + # 使用示例 if __name__ == "__main__": # 获取设备ID(示例) diff --git a/scheduler.py b/scheduler.py new file mode 100644 index 0000000..c59bbf6 --- /dev/null +++ b/scheduler.py @@ -0,0 +1,100 @@ +import requests +import time +from concurrent.futures import ThreadPoolExecutor, as_completed +# 从你的 main.py 中导入DeviceAutomation类 +from main import DeviceAutomation +import globals.apis as apis +from datetime import datetime + +## --- 配置区 --- +API_URL = "http://your-api-server.com/api/tasks" # 替换为真实的接口地址 +MAX_WORKERS = 3 # 最大并发线程数,建议根据网络带宽调整 +DEFAULT_PORT = "6666" + +def get_remote_tasks(): + """ + 从接口获取数据 + """ + try: + # 如果是真实接口,取消下面两行的注释 + # response = requests.get(API_URL, timeout=10) + # return response.json() + accounts = apis.get_accounts_from_server("68c0dbfdb7cbcd616e7c5ab5") + if not accounts: + print("❌ 未从服务器获取到账户信息,终止流程") + return {} + + filtered_accounts = [account for account in accounts if account.get('is_ok') == 1] + # print("✅ 获取账户信息成功", filtered_accounts) + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + # 从原始 accounts 数据中筛选有 device_ip 和 device_port 且不为 None 的账户 + device_dict = {} + + for account in filtered_accounts: + device_ip = account.get('device_ip') + device_port = account.get('device_port') + + # 检查 device_ip 和 device_port 是否都存在且不为 None + if device_ip and device_port: + # 拼接为 ip:port 格式 + key = f"{device_ip}:{device_port}" + # 添加当前时间 + device_dict[key] = current_time + + # 结果 + print(device_dict) + return device_dict + # # 模拟数据供测试 + # return { + # "192.168.1.45:5555": "2026-02-03 10:20:00", + # "192.168.1.100:5556": "2026-02-03 10:20:20", + # "192.168.31.12:5557": "2026-02-03 10:21:00" + # } + except Exception as e: + print(f"❌ 获取接口任务失败: {e}") + return {} + +def run_task(address, target_time): + """ + 单个线程的任务包装器 + """ + # 格式化账号/设备名:确保带上端口号 + device_address = address + + print(f"🕒 [等待/启动] 设备: {device_address} | 预定时间: {target_time}") + + try: + # 创建DeviceAutomation实例并执行上传逻辑 + automation = DeviceAutomation(device_address) + result = automation.handle_app_state() + return f"✅ {device_address} 完成: {result}" + except Exception as e: + return f"❌ {device_address} 报错: {str(e)}" + +def monitor_center(): + """调度中心""" + tasks_data = get_remote_tasks() + + if not tasks_data: + print("📭 接口未返回任何任务,程序退出。") + return + + print(f"🗂️ 发现 {len(tasks_data)} 个待处理账号,开始建立线程池...") + + # 使用线程池并发执行 + with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: + # 提交所有任务 + future_to_device = { + executor.submit(run_task, acc, t): acc + for acc, t in tasks_data.items() + } + + # 实时打印完成情况 + for future in as_completed(future_to_device): + print(future.result()) + +if __name__ == "__main__": + print("🚀 自动化调度程序启动...") + monitor_center() + print("🏁 所有并发任务处理序列结束。") \ No newline at end of file diff --git a/test_results/上传失败的断点.txt b/test_results/上传失败的断点.txt index 62e0a65..fbf556e 100644 --- a/test_results/上传失败的断点.txt +++ b/test_results/上传失败的断点.txt @@ -1,5 +1,9 @@ -CDWZQ-3标-雷庙村大桥-8-号桥台-平原 -CDWZQ-3标-雷庙村特大桥-5-6-号墩-平原 -CDWZQ-3标-金马村特大桥-14-号墩-平原 -CDWZQ-3标-雷庙村特大桥-4#-7-14号墩-平原 -CDWZQ-3标-老屋坡特大桥-14-21-平原 +CDWZQ-2标-区间路基135号-447663-447219-平原 +CDWZQ-2标-蓝家湾特大桥17#墩-448591-0-平原 +CDWZQ-2标-蓝家湾特大桥-0桥台-0-平原 +CDWZQ-2标-一工区-资阳沱江特大桥-10#墩-平原 +CDWZQ-2标-蓝家湾特大桥-1#-6-平原 +CDWZQ-2标-蓝家湾特大桥-19#-号墩-平原 +CDWZQ-2标-一工区-龙家沟右线大桥-0-11号墩-山区 +CDWZQ-2标-二工区-蓝家湾特大桥-18#-山区 +CDWZQ-2标-一工区-资阳沱江特大桥-9#-山区