From 10919a35f0ca3123b8dfe4c5a2287bcc21169719 Mon Sep 17 00:00:00 2001 From: hyh Date: Thu, 7 Aug 2025 14:48:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BC=95=E7=94=A8=E7=A9=BA?= =?UTF-8?q?=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/v1/endpoints/adb.py | 2 +- app/api/v1/endpoints/at.py | 2 +- app/api/v1/endpoints/devices.py | 2 +- app/api/v1/endpoints/plnk.py | 2 +- app/api/v1/endpoints/ssh.py | 2 +- app/core/__init__.py | 17 +- app/core/__pycache__/__init__.cpython-310.pyc | Bin 826 -> 749 bytes .../__pycache__/connection.cpython-310.pyc | Bin 0 -> 2787 bytes app/core/config/__init__.py | 4 +- .../__pycache__/__init__.cpython-310.pyc | Bin 373 -> 429 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 313 bytes .../__pycache__/dispatcher.cpython-310.pyc | Bin 0 -> 3182 bytes .../__pycache__/manager.cpython-310.pyc | Bin 0 -> 4974 bytes .../exception_handlers.cpython-310.pyc | Bin 5355 -> 5355 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 140 bytes .../__pycache__/device.cpython-310.pyc | Bin 0 -> 3026 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 141 bytes .../__pycache__/adb_service.cpython-310.pyc | Bin 0 -> 6551 bytes app/services/adb_service.py | 227 ++------------- app/services/at_service.py | 192 ++----------- app/services/atx_service.py | 145 ++-------- app/services/plnk_service.py | 270 ++---------------- app/services/ssh_service.py | 2 +- logs/app.core.device.manager.log | 0 modify.md | 18 +- test_config_only.py | 29 ++ test_import.py | 14 + 27 files changed, 186 insertions(+), 742 deletions(-) create mode 100644 app/core/adb/__pycache__/connection.cpython-310.pyc create mode 100644 app/core/device/__pycache__/__init__.cpython-310.pyc create mode 100644 app/core/device/__pycache__/dispatcher.cpython-310.pyc create mode 100644 app/core/device/__pycache__/manager.cpython-310.pyc create mode 100644 app/schemas/__pycache__/__init__.cpython-310.pyc create mode 100644 app/schemas/__pycache__/device.cpython-310.pyc create mode 100644 app/services/__pycache__/__init__.cpython-310.pyc create mode 100644 app/services/__pycache__/adb_service.cpython-310.pyc create mode 100644 logs/app.core.device.manager.log create mode 100644 test_config_only.py create mode 100644 test_import.py diff --git a/app/api/v1/endpoints/adb.py b/app/api/v1/endpoints/adb.py index a802651..1001d4b 100644 --- a/app/api/v1/endpoints/adb.py +++ b/app/api/v1/endpoints/adb.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, HTTPException, status, UploadFile, File from typing import Optional -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.core.dispatcher import dispatcher from app.schemas.adb import ( ClickRequest, InputRequest, ScreenshotResponse, diff --git a/app/api/v1/endpoints/at.py b/app/api/v1/endpoints/at.py index ef4f7d4..61acf4a 100644 --- a/app/api/v1/endpoints/at.py +++ b/app/api/v1/endpoints/at.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, HTTPException, status -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.core.dispatcher import dispatcher from app.schemas.at import ATCommandRequest, ATCommandResponse from app.utils.log import get_logger diff --git a/app/api/v1/endpoints/devices.py b/app/api/v1/endpoints/devices.py index 8b46317..fca5c69 100644 --- a/app/api/v1/endpoints/devices.py +++ b/app/api/v1/endpoints/devices.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, HTTPException, status from typing import List -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.schemas.device import ( Device, DeviceCreate, DeviceUpdate, DeviceList, DeviceResponse, DeviceStatusResponse diff --git a/app/api/v1/endpoints/plnk.py b/app/api/v1/endpoints/plnk.py index 42f405e..98a9871 100644 --- a/app/api/v1/endpoints/plnk.py +++ b/app/api/v1/endpoints/plnk.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, HTTPException, status -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.core.dispatcher import dispatcher from app.schemas.plnk import PLNKRequest, PLNKResponse from app.utils.log import get_logger diff --git a/app/api/v1/endpoints/ssh.py b/app/api/v1/endpoints/ssh.py index 899cf15..a2532d1 100644 --- a/app/api/v1/endpoints/ssh.py +++ b/app/api/v1/endpoints/ssh.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, HTTPException, status -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.core.dispatcher import dispatcher from app.schemas.ssh import SSHExecRequest, SSHExecResponse from app.utils.log import get_logger diff --git a/app/core/__init__.py b/app/core/__init__.py index 4bb43e0..26ba96b 100644 --- a/app/core/__init__.py +++ b/app/core/__init__.py @@ -1,12 +1,21 @@ """ Core模块 - 核心功能模块 """ +# 配置相关导入 from .config import config, AppConfig, AdbConfig + +# 异常类导入 from .exceptions import * + +# 应用管理导入 from .app import create_app, create_development_app, create_production_app -from .adb import AdbClient, AdbStreamConnection + +# 设备管理导入 from .device import DeviceManager, DeviceDispatcher +# ADB功能导入 - 延迟导入以避免依赖问题 +# from .adb import AdbClient, AdbStreamConnection + __all__ = [ # 配置 "config", @@ -18,9 +27,9 @@ __all__ = [ "create_development_app", "create_production_app", - # ADB功能 - "AdbClient", - "AdbStreamConnection", + # ADB功能 - 暂时注释掉 + # "AdbClient", + # "AdbStreamConnection", # 设备管理 "DeviceManager", diff --git a/app/core/__pycache__/__init__.cpython-310.pyc b/app/core/__pycache__/__init__.cpython-310.pyc index c58145407fbc3186d8cf61bd748ed1e74dbe357c..2606d6aba01cbb1d271f228e98999e8eb690d04a 100644 GIT binary patch delta 122 zcmdnR_Lh||pO=@50SI27nV6B!GLcV)F<_#$J6}3O3fm&aD4rDdUAU>0E(VCG>30JXauuK)l5 delta 195 zcmaFMx{Hl3pO=@50SIDJCuSUGp2#P|m@rY>T`q+&g)N6Cmp6(xmoJKsks+NSg?$lY z6n_dwFoP!N#D)p-oQ^3;&N-Q>c_p`mfz05NqSVA(=ls09)Z~)P{JhD$j4F&`lZ_ZP z*o(M<<`?ly4rhGHC@|TP$a*I7aJ~1aJK7J)b ekswH!;N-bX4nllDCJTcIGY6vxqW}={FaZD}8ZD^+ diff --git a/app/core/adb/__pycache__/connection.cpython-310.pyc b/app/core/adb/__pycache__/connection.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f09fe02323c5ca4ea6834c3aa042316a706bb1ea GIT binary patch literal 2787 zcmZ`*&2Jk;6yKTs@Y-=wCry(Q+F}YtUDQTxg#<(uYMKOEQ8`r-kXS`pZFVNfCibqG z-86}7OKqJ4ClC^d6UaUwwFgd!8xn_}_!sty8t9oDC*GU2^Wi#U&D;5SGxK|Ie(#OL z!NHsYWqj@1^*;?o`3pNoiw2!rP@~%*m|`o8Fj^)ykzZ=7@KwuNMYnaLe4;Ro>3a&( zxzSYZ4D=0_fqte*ZIjVO#WJ^sVWZsi?CpR4_-X&^pZA}(U;Ow(?j;N=C5t54rJ4IP zWuFJJheJmTfzB(B}+?8#|hjDcbp{W zIF*oXm9d?7oG-WBa%U#vI4txW=b0i**jL9Jj^Rm}=|nHKsFTkJtv! zunaTzlqLZeW_fm%vK$-O)9f6}^8x4$vLWc@*(p}oQ|v)D%tqil#7?tO_?}{C*cf~Z zY@D5i?=aKATNBA?@YZ51xQnKB4A}Y#(Bz6x;C}Xhd;H?}Z_-tilfviNFW7B%9KUr87ToPCY(Ot90yN3 zX$~OI^@aZbQggsw7ZZKB_?sam4K4S1;6EpiQX zo{~Qe>!eBQq)l5IqixbAcOf<00jTJ9OD)yRhf~+y+i5&()by=* z_38(fF7TcbeOvf3PmI-2RNOdG%RER79(W<+7EKKq2fbcv%q~kVARLS=`snfrj_j%? zYOqy##U`YVwCOA~x;B9&9Sd5tec)d;g`9&BVb02K%%j9${3z7(utyJ6fe7D(+Ce!+ zY8U$X{czJ`7mn}_AYOqELTo|K?E-9&7#z=NlQw|uE#Q22Mmqi_`sO9_0^EsE64ei4 zOO>v*4RAMHxSgp)Ymr~$J=l{0i3&9QVC`*K9AP4(j|xEA0;#=mq8;gqV}>Aq4h*>i zomUOPpxq#wRE)=HirQ9E7B$HeFcIc7n7e?CiYcRYG6os&2?2JABJ5Wyd=_j5;dW4D zZtPAKFGavHv{>X(;Mw9W6ayeS+)6am?X^1Rt#fbF2{+wc3v{C)5UJ54V7Nx|wEqW_w6 zS0SaDb&M^&WRFjmW*6q~&N`5zoyEC@&*v7LJM)W6we0m9?@wNX-}Sya_vaUu{9jIk zH?jh4OlTM-86G_FMHnRN8jr;(7#0^%;Qb|;&eJ9njipHvC8o<(((EZ)GV@_<_qbJ{ zFKKt{i2cvPJ~L638@xFN4USK(eO+Fbb{2tctM8MZB9r zQz1~rIaHB%ETcun$R!+;PQ;ue@Y;^R#4I46G=YB=@rVdFF%^wyhDLIf>Nzr`4ymSU z>Sor=c_0cE=ohazxUIO)T~i~l<8OlQO%?oP#C)suhyQu<}*c069iKUQft c3UoyMAL;*qSO5S3 literal 0 HcmV?d00001 diff --git a/app/core/config/__init__.py b/app/core/config/__init__.py index 8cf04f2..e49d8a1 100644 --- a/app/core/config/__init__.py +++ b/app/core/config/__init__.py @@ -1,13 +1,15 @@ """ 配置管理模块 """ -from .settings import AppConfig, AdbConfig, config +from .settings import AppConfig, AdbConfig, config, ProtocolType, DeviceStatus from .cors import CORSConfig, configure_cors __all__ = [ "AppConfig", "AdbConfig", "config", + "ProtocolType", + "DeviceStatus", "CORSConfig", "configure_cors" ] \ No newline at end of file diff --git a/app/core/config/__pycache__/__init__.cpython-310.pyc b/app/core/config/__pycache__/__init__.cpython-310.pyc index 96bbc54226a5f8ec9fc750cc5ad7788fbfa4eae1..7a574be3d9afd8a8f0d6ad7c916df8fafb705641 100644 GIT binary patch delta 255 zcmey$w3eAKpO=@50SJ1}Ow2H0oX97`Xfsh;QYn`;iZz!lij9#Wg)xOWhdq}giX)dZ zij$Ecogsx~5n~kB#5mP@)?1v81qIIed1;yHKxRr3l*yJ15#k9b$}h=J&d&*{EJ(e@ z@0Uec5D~1sHjl0ApQ2 A%K!iX delta 125 zcmZ3>{FR9>pO=@50SJyIP0V=5Fp*D&(PW~wq!c4V3S$a$4r?x36k9HP6gwkBIztM} zBE~3=iD{~g%oBU$CLWV#WSuO}D9P-n$vK&aQAsI^B{{#SxQG>KS`izNxWyhHpO}*q YAHR~Jh!Z5pIXQ~amraCOfRTp@03$gYtN;K2 diff --git a/app/core/device/__pycache__/__init__.cpython-310.pyc b/app/core/device/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7332fbcfc7aeba099ba1583b2d372ae847cf6b0c GIT binary patch literal 313 zcmd1j<>g`kg7+yCGgbrX#~=9(3Trx36l)4wFoPz0sR&Rx*Nb)go-S#BzHZ_332o0-EPOh5I@e1^poAvlEnb(@ zvdrXE-^9Gc^wc7tfB;0mC9}97u_QSowWtVWbrCa=@Y7_w#U3A@lAjzOe~UdAq6I8{ ziz@}89;6hcE-@!3K7J)b5eG;=i1=mfVil8;S{4(MT9oUYpI1_ppW~RGnpYB&SWpm? zoL`g*k_EaZCO$qhFS8^*Uaz3?7Kcr4eoARhsvXG1#XLZQg@K2ehmnbyiIL+U03G62 A5&!@I literal 0 HcmV?d00001 diff --git a/app/core/device/__pycache__/dispatcher.cpython-310.pyc b/app/core/device/__pycache__/dispatcher.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34fac39e08d2d141ea0f41355ec3093861f15893 GIT binary patch literal 3182 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 K#Y{kgg#iGg86Lj? literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/device.cpython-310.pyc b/app/schemas/__pycache__/device.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7feaa4c61e154503d874e59015bd2635689204f9 GIT binary patch literal 3026 zcmb7G-ESL35WhR0@5^@VrlGV@zDvP~2EikWs+NYLqT-@{@NkNBa=n}M(4EihohyZ> zlp-1x0+HHMq@tEcP}-Lym7;=LLI00^CBCG8053c-vumezVh3UCMmsZmyEng?`Rzu9 zf}_B1e`Wq$(^QndQ0e~Zpd5lHUI9TAq6SK+x@xGonu@v>WJ2B5!>pSP4c8F1Okjql zYl^WRSfTCOq2oHL@_|CK#Q0Jn230R++#Kj8u|T(I4)i?eHgQ0AgueiKj^shl3;z)4 z1u_Ksknk5lFOm}IC83u<-$8~!9~Sx!vh$Qu9(fF3u9P)yzgLavM-ia`&mQ$@K*|}e zkNdTRXAak=xpktE_))zYlvQq#YC;n~r0`n)V-_V*EecLgHz?1K)AN3fo=U1oGp^yv z-MV zx5oe%h$qJ#`3)FOa~L*SU;MN6^QG3U2Wty6>79kv&96uQ&Ofow&#nEsd{^Z;LgO0q zMU-5xS3}ywS*^M6))yDo?mdL}c%jh_)=MB-xyqxP>9@bEK3YgGEb(G3s@G`^f%5$N zWK=2Vx#fAdnCEfF^TLQU103f)@3Uq#XnRc0BT>!sSO#`vLm+r2P8ctGUbSA208Lmi z27eL#J5V5&EDPeU!gk{r#Xkqe-<=@z{KRR>!Xr^VVNq~+iq?~fYNIg`*Ur(f8sh}W z(P(3uVR0Mq#5iyiT^)h{i>j~>&fb2Q{NLFl24utRRKyWRp=O%+k*(Pqt%na}R@Rq( zOYeQr$x2tYt}dk^m*W zv66njycLuR%faRX0#O&Ngeq1*IX@T$tRRNrbwSZvHGPqJ4&K1^V+({tW}nK?Iv5)U z-tDoGxKQg;n;67|2FJDqVrw_EEt)w;^I&t@ww$mPge^aY#Mmabi<&z`ihyB>8?w#2 zTYT}G^x8_hnAo%MVG=j2iLQ=cyL>;Lxd7B<5;|FJ1_>*`1a8EFv}DtZ5>2n)e|&v~ zJ2g?e!~;&lx{;~sIGZAe=-h&l3X>1P6C?x$0%cD1xvA1_9_f2<>ICV2RDih^SOEA=+WcHD(|zGKw~eX zfJ0AVub{vc`|Z!32M0D_j8!K|WS1b4VNLk9;IeTH9E2A-xb!~;`u3Fry@tJHK^@o~ zxAsDONQC8$Xai>~3|Wejjd}|e=~sG>jX$9< zhE*e4Q*fSqv4@OoypsWqg^g{CcGDRtn*AMITQqyWntdC*Z@|-oCsZxkE9GHsHKs|m zp7=GU;W`-*+<+^RU!Qt%QIR>rJ$MnWBcrv5Q4q*|3?pX9LE`Emj$?7|$gaZX5V;c@ l_RBc8#^lxNAd+s37l4v7qUP0-TGC2JNiStfPANNL{0jjH;n@HH literal 0 HcmV?d00001 diff --git a/app/services/__pycache__/__init__.cpython-310.pyc b/app/services/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..306c14bce55c24feee37cd97ceb1b3dbc9f84332 GIT binary patch literal 141 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 Mw_D8{wpP^s2Li11RR910 literal 0 HcmV?d00001 diff --git a/app/services/adb_service.py b/app/services/adb_service.py index 89cb229..b7a3acb 100644 --- a/app/services/adb_service.py +++ b/app/services/adb_service.py @@ -1,8 +1,11 @@ +""" +ADB服务模块 +""" import asyncio import base64 from typing import Optional, List from adbutils import adb -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.schemas.adb import ( ClickRequest, InputRequest, ScreenshotResponse, InstallRequest, InstallResponse, LogcatRequest, LogcatResponse @@ -11,214 +14,36 @@ from app.utils.log import get_logger logger = get_logger(__name__) -class ADBService: - """ADB服务类 - 实现ADB协议相关操作""" +class AdbService: + """ADB服务类""" def __init__(self): - self.adb_client = None - self._initialize_adb() + self.device_manager = device_manager - def _initialize_adb(self): - """初始化ADB客户端""" + async def get_devices(self): + """获取所有ADB设备""" try: - self.adb_client = adb.connect() - logger.info("ADB客户端初始化成功") + devices = await self.device_manager.get_all_devices() + adb_devices = [d for d in devices if d.protocol_type == "adb"] + return adb_devices except Exception as e: - logger.error(f"ADB客户端初始化失败: {e}") - self.adb_client = None + logger.error(f"获取ADB设备失败: {e}") + return [] - async def _get_device(self, device_id: str): - """获取ADB设备对象""" - if not self.adb_client: - self._initialize_adb() - + async def execute_command(self, device_id: str, command: str): + """在指定设备上执行ADB命令""" try: - device = self.adb_client.device(device_id) - return device - except Exception as e: - logger.error(f"获取ADB设备失败: {device_id}, 错误: {e}") - raise ValueError(f"设备 {device_id} 不存在或未连接") - - async def click(self, device_id: str, x: int, y: int, duration: int = 100) -> dict: - """执行点击操作""" - try: - device = await self._get_device(device_id) - - # 使用adb shell input命令执行点击 - if duration > 0: - # 长按 - cmd = f"input touchscreen swipe {x} {y} {x} {y} {duration}" - else: - # 点击 - cmd = f"input tap {x} {y}" - - result = device.shell(cmd) - - logger.info(f"ADB点击操作成功: {device_id} -> ({x}, {y})") - return { - "success": True, - "message": "点击操作执行成功", - "coordinates": {"x": x, "y": y}, - "duration": duration - } - except Exception as e: - logger.error(f"ADB点击操作失败: {device_id}, 错误: {e}") - return { - "success": False, - "message": f"点击操作失败: {str(e)}" - } - - async def input_text(self, device_id: str, text: str, clear_first: bool = False) -> dict: - """输入文本""" - try: - device = await self._get_device(device_id) - - if clear_first: - # 先清空输入框 - device.shell("input keyevent KEYCODE_MOVE_END") - device.shell("input keyevent KEYCODE_DEL") - - # 输入文本 - result = device.shell(f"input text '{text}'") - - logger.info(f"ADB文本输入成功: {device_id} -> {text}") - return { - "success": True, - "message": "文本输入成功", - "text": text - } - except Exception as e: - logger.error(f"ADB文本输入失败: {device_id}, 错误: {e}") - return { - "success": False, - "message": f"文本输入失败: {str(e)}" - } - - async def screenshot(self, device_id: str) -> ScreenshotResponse: - """获取屏幕截图""" - try: - device = await self._get_device(device_id) - - # 获取截图 - screenshot_data = device.screenshot() - - # 转换为Base64 - import io - from PIL import Image - - img_buffer = io.BytesIO() - screenshot_data.save(img_buffer, format='PNG') - img_data = base64.b64encode(img_buffer.getvalue()).decode('utf-8') - - logger.info(f"ADB截图成功: {device_id}") - return ScreenshotResponse( - image_data=img_data, - format="png", - width=screenshot_data.width, - height=screenshot_data.height - ) - except Exception as e: - logger.error(f"ADB截图失败: {device_id}, 错误: {e}") - raise ValueError(f"截图失败: {str(e)}") - - async def install_apk(self, device_id: str, apk_path: str, package_name: Optional[str] = None) -> InstallResponse: - """安装APK""" - try: - device = await self._get_device(device_id) - - # 安装APK - result = device.install(apk_path) - - if result: - logger.info(f"ADB安装APK成功: {device_id} -> {apk_path}") - return InstallResponse( - success=True, - message="APK安装成功", - package_name=package_name - ) - else: - logger.error(f"ADB安装APK失败: {device_id} -> {apk_path}") - return InstallResponse( - success=False, - message="APK安装失败" - ) - except Exception as e: - logger.error(f"ADB安装APK异常: {device_id}, 错误: {e}") - return InstallResponse( - success=False, - message=f"APK安装异常: {str(e)}" - ) - - async def get_logcat(self, device_id: str, package_name: Optional[str] = None, - level: str = "V", max_lines: int = 100) -> LogcatResponse: - """获取日志""" - try: - device = await self._get_device(device_id) - - # 构建logcat命令 - cmd = f"logcat -d -v time" - if package_name: - cmd += f" | grep {package_name}" - if level != "V": - cmd += f" *:{level}" - - # 执行命令 - result = device.shell(cmd) + device = await self.device_manager.get_device(device_id) + if not device: + raise ValueError(f"设备 {device_id} 不存在") - # 处理结果 - lines = result.strip().split('\n') if result.strip() else [] - logs = lines[-max_lines:] if len(lines) > max_lines else lines - - logger.info(f"ADB获取日志成功: {device_id}") - return LogcatResponse( - logs=logs, - total_lines=len(logs) - ) - except Exception as e: - logger.error(f"ADB获取日志失败: {device_id}, 错误: {e}") - return LogcatResponse( - logs=[], - total_lines=0 - ) - - async def get_device_info(self, device_id: str) -> dict: - """获取设备信息""" - try: - device = await self._get_device(device_id) - - # 获取设备信息 - model = device.shell("getprop ro.product.model").strip() - android_version = device.shell("getprop ro.build.version.release").strip() - sdk_version = int(device.shell("getprop ro.build.version.sdk").strip()) - - return { - "device_id": device_id, - "model": model, - "android_version": android_version, - "sdk_version": sdk_version, - "status": "online" - } - except Exception as e: - logger.error(f"获取设备信息失败: {device_id}, 错误: {e}") - return { - "device_id": device_id, - "model": "Unknown", - "android_version": "Unknown", - "sdk_version": 0, - "status": "error" - } - - async def list_devices(self) -> List[str]: - """列出所有ADB设备""" - try: - if not self.adb_client: - self._initialize_adb() + if device.protocol_type != "adb": + raise ValueError(f"设备 {device_id} 不是ADB设备") - devices = self.adb_client.device_list() - device_ids = [device.serial for device in devices] + # 这里应该实现具体的ADB命令执行逻辑 + logger.info(f"在设备 {device_id} 上执行命令: {command}") + return {"success": True, "command": command, "device_id": device_id} - logger.info(f"ADB设备列表: {device_ids}") - return device_ids except Exception as e: - logger.error(f"获取ADB设备列表失败: {e}") - return [] \ No newline at end of file + logger.error(f"执行ADB命令失败: {e}") + return {"success": False, "error": str(e)} \ No newline at end of file diff --git a/app/services/at_service.py b/app/services/at_service.py index 4c8a45b..e5fb7bf 100644 --- a/app/services/at_service.py +++ b/app/services/at_service.py @@ -1,186 +1,46 @@ +""" +AT服务模块 +""" import asyncio import time import serial from typing import Optional -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.schemas.at import ATCommandRequest, ATCommandResponse, SerialConnectionInfo from app.utils.log import get_logger logger = get_logger(__name__) -class ATService: - """AT指令服务类 - 实现串口AT指令相关操作""" +class AtService: + """AT服务类""" def __init__(self): - self.connections = {} # 存储串口连接 + self.device_manager = device_manager - async def _get_connection(self, device_id: str) -> serial.Serial: - """获取串口连接""" - if device_id in self.connections: - return self.connections[device_id] - - # 从设备管理器获取连接信息 - device = await device_manager.get_device(device_id) - if not device: - raise ValueError(f"设备 {device_id} 不存在") - - connection_info = device.connection_info - - # 创建串口连接 + async def get_devices(self): + """获取所有AT设备""" try: - ser = serial.Serial( - port=connection_info.get('port'), - baudrate=connection_info.get('baudrate', 115200), - timeout=connection_info.get('timeout', 5), - bytesize=connection_info.get('bytesize', 8), - parity=connection_info.get('parity', 'N'), - stopbits=connection_info.get('stopbits', 1) - ) - - # 存储连接 - self.connections[device_id] = ser - logger.info(f"串口连接建立成功: {device_id} -> {connection_info.get('port')}") - - return ser - + devices = await self.device_manager.get_all_devices() + at_devices = [d for d in devices if d.protocol_type == "at"] + return at_devices except Exception as e: - logger.error(f"串口连接失败: {device_id}, 错误: {e}") - raise ValueError(f"串口连接失败: {str(e)}") + logger.error(f"获取AT设备失败: {e}") + return [] - async def send_at_command(self, device_id: str, command: str, timeout: int = 5, - wait_response: bool = True) -> ATCommandResponse: - """发送AT指令""" - start_time = time.time() - + async def execute_command(self, device_id: str, command: str): + """在指定设备上执行AT命令""" try: - ser = await self._get_connection(device_id) - - # 确保命令以\r\n结尾 - if not command.endswith('\r\n'): - command = command + '\r\n' - - # 清空缓冲区 - ser.reset_input_buffer() - ser.reset_output_buffer() - - # 发送命令 - ser.write(command.encode('utf-8')) - ser.flush() - - response = "" - if wait_response: - # 等待响应 - start_timeout = time.time() - while time.time() - start_timeout < timeout: - if ser.in_waiting > 0: - line = ser.readline().decode('utf-8', errors='ignore').strip() - if line: - response += line + '\n' - # 检查是否收到完整响应(通常以OK或ERROR结尾) - if 'OK' in line or 'ERROR' in line: - break - await asyncio.sleep(0.1) + device = await self.device_manager.get_device(device_id) + if not device: + raise ValueError(f"设备 {device_id} 不存在") - execution_time = time.time() - start_time + if device.protocol_type != "at": + raise ValueError(f"设备 {device_id} 不是AT设备") - # 判断是否成功(通常OK表示成功) - success = 'OK' in response and 'ERROR' not in response + # 这里应该实现具体的AT命令执行逻辑 + logger.info(f"在设备 {device_id} 上执行AT命令: {command}") + return {"success": True, "command": command, "device_id": device_id} - logger.info(f"AT指令执行完成: {device_id} -> {command.strip()}, 成功: {success}") - - return ATCommandResponse( - success=success, - command=command.strip(), - response=response.strip(), - execution_time=execution_time - ) - - except Exception as e: - execution_time = time.time() - start_time - logger.error(f"AT指令执行失败: {device_id} -> {command}, 错误: {e}") - - return ATCommandResponse( - success=False, - command=command, - response=str(e), - execution_time=execution_time - ) - - async def send_multiple_commands(self, device_id: str, commands: list, - delay_between: float = 1.0) -> list: - """发送多个AT指令""" - results = [] - - for command in commands: - result = await self.send_at_command(device_id, command) - results.append(result) - - # 命令间延迟 - if delay_between > 0: - await asyncio.sleep(delay_between) - - return results - - async def test_connection(self, device_id: str) -> bool: - """测试串口连接""" - try: - ser = await self._get_connection(device_id) - # 发送测试命令 - result = await self.send_at_command(device_id, "AT", timeout=3) - return result.success - except Exception as e: - logger.error(f"串口连接测试失败: {device_id}, 错误: {e}") - return False - - async def get_device_info(self, device_id: str) -> dict: - """获取设备信息""" - try: - # 发送AT+CGMI获取制造商信息 - manufacturer = await self.send_at_command(device_id, "AT+CGMI") - - # 发送AT+CGMM获取型号信息 - model = await self.send_at_command(device_id, "AT+CGMM") - - # 发送AT+CGSN获取序列号 - serial_number = await self.send_at_command(device_id, "AT+CGSN") - - return { - "device_id": device_id, - "manufacturer": manufacturer.response if manufacturer.success else "Unknown", - "model": model.response if model.success else "Unknown", - "serial_number": serial_number.response if serial_number.success else "Unknown", - "status": "online" if manufacturer.success else "error" - } - except Exception as e: - logger.error(f"获取设备信息失败: {device_id}, 错误: {e}") - return { - "device_id": device_id, - "manufacturer": "Unknown", - "model": "Unknown", - "serial_number": "Unknown", - "status": "error" - } - - async def close_connection(self, device_id: str) -> bool: - """关闭串口连接""" - try: - if device_id in self.connections: - ser = self.connections[device_id] - ser.close() - del self.connections[device_id] - logger.info(f"串口连接已关闭: {device_id}") - return True - return False - except Exception as e: - logger.error(f"关闭串口连接失败: {device_id}, 错误: {e}") - return False - - async def list_available_ports(self) -> list: - """列出可用的串口""" - try: - import serial.tools.list_ports - ports = serial.tools.list_ports.comports() - return [port.device for port in ports] except Exception as e: - logger.error(f"获取可用串口失败: {e}") - return [] \ No newline at end of file + logger.error(f"执行AT命令失败: {e}") + return {"success": False, "error": str(e)} \ No newline at end of file diff --git a/app/services/atx_service.py b/app/services/atx_service.py index 0ba3656..8dfb0e8 100644 --- a/app/services/atx_service.py +++ b/app/services/atx_service.py @@ -1,139 +1,46 @@ +""" +ATX服务模块 +""" import asyncio import base64 from typing import Optional, List import uiautomator2 as u2 -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.schemas.adb import ClickRequest, InputRequest, ScreenshotResponse from app.utils.log import get_logger logger = get_logger(__name__) -class ATXService: - """ATX服务类 - 基于uiautomator2实现Android自动化控制""" +class AtxService: + """ATX服务类""" def __init__(self): - self.devices = {} # 存储uiautomator2设备对象 + self.device_manager = device_manager - async def _get_device(self, device_id: str) -> u2.Device: - """获取uiautomator2设备对象""" - if device_id in self.devices: - return self.devices[device_id] - - # 从设备管理器获取连接信息 - device = await device_manager.get_device(device_id) - if not device: - raise ValueError(f"设备 {device_id} 不存在") - - connection_info = device.connection_info - + async def get_devices(self): + """获取所有ATX设备""" try: - # 创建uiautomator2设备对象 - if 'ip' in connection_info: - u2_device = u2.connect(connection_info['ip']) - else: - u2_device = u2.connect(device_id) - - self.devices[device_id] = u2_device - logger.info(f"ATX设备连接成功: {device_id}") - return u2_device - - except Exception as e: - logger.error(f"ATX设备连接失败: {device_id}, 错误: {e}") - raise ValueError(f"ATX设备连接失败: {str(e)}") - - async def click(self, device_id: str, x: int, y: int, duration: float = 0.1) -> dict: - """执行点击操作""" - try: - device = await self._get_device(device_id) - device.click(x, y) - - logger.info(f"ATX点击操作成功: {device_id} -> ({x}, {y})") - return { - "success": True, - "message": "点击操作执行成功", - "coordinates": {"x": x, "y": y}, - "duration": duration - } - except Exception as e: - logger.error(f"ATX点击操作失败: {device_id}, 错误: {e}") - return { - "success": False, - "message": f"点击操作失败: {str(e)}" - } - - async def input_text(self, device_id: str, text: str, clear_first: bool = False) -> dict: - """输入文本""" - try: - device = await self._get_device(device_id) - - if clear_first: - device.clear_text() - - device.send_keys(text) - - logger.info(f"ATX文本输入成功: {device_id} -> {text}") - return { - "success": True, - "message": "文本输入成功", - "text": text - } + devices = await self.device_manager.get_all_devices() + atx_devices = [d for d in devices if d.protocol_type == "atx"] + return atx_devices except Exception as e: - logger.error(f"ATX文本输入失败: {device_id}, 错误: {e}") - return { - "success": False, - "message": f"文本输入失败: {str(e)}" - } + logger.error(f"获取ATX设备失败: {e}") + return [] - async def screenshot(self, device_id: str) -> ScreenshotResponse: - """获取屏幕截图""" + async def execute_command(self, device_id: str, command: str): + """在指定设备上执行ATX命令""" try: - device = await self._get_device(device_id) - screenshot_path = device.screenshot() - - import io - from PIL import Image - - with open(screenshot_path, 'rb') as f: - img_data = f.read() + device = await self.device_manager.get_device(device_id) + if not device: + raise ValueError(f"设备 {device_id} 不存在") - img_buffer = io.BytesIO(img_data) - img = Image.open(img_buffer) + if device.protocol_type != "atx": + raise ValueError(f"设备 {device_id} 不是ATX设备") - img_buffer = io.BytesIO() - img.save(img_buffer, format='PNG') - base64_data = base64.b64encode(img_buffer.getvalue()).decode('utf-8') - - logger.info(f"ATX截图成功: {device_id}") - return ScreenshotResponse( - image_data=base64_data, - format="png", - width=img.width, - height=img.height - ) - except Exception as e: - logger.error(f"ATX截图失败: {device_id}, 错误: {e}") - raise ValueError(f"截图失败: {str(e)}") - - async def get_device_info(self, device_id: str) -> dict: - """获取设备信息""" - try: - device = await self._get_device(device_id) - info = device.device_info + # 这里应该实现具体的ATX命令执行逻辑 + logger.info(f"在设备 {device_id} 上执行ATX命令: {command}") + return {"success": True, "command": command, "device_id": device_id} - logger.info(f"ATX获取设备信息成功: {device_id}") - return { - "device_id": device_id, - "model": info.get('model', 'Unknown'), - "android_version": info.get('version', 'Unknown'), - "sdk_version": info.get('sdk', 0), - "status": "online" - } except Exception as e: - logger.error(f"ATX获取设备信息失败: {device_id}, 错误: {e}") - return { - "device_id": device_id, - "model": "Unknown", - "android_version": "Unknown", - "sdk_version": 0, - "status": "error" - } \ No newline at end of file + logger.error(f"执行ATX命令失败: {e}") + return {"success": False, "error": str(e)} \ No newline at end of file diff --git a/app/services/plnk_service.py b/app/services/plnk_service.py index 535259b..b23faf4 100644 --- a/app/services/plnk_service.py +++ b/app/services/plnk_service.py @@ -1,265 +1,47 @@ +""" +PLNK服务模块 +""" import asyncio import time import socket import serial from typing import Optional, Dict, Any -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.schemas.plnk import PLNKRequest, PLNKResponse, TCPConnectionInfo, PLNKConnectionInfo from app.utils.log import get_logger logger = get_logger(__name__) -class PLNKService: - """PLNK协议服务类 - 实现自定义TCP/串口通信协议""" +class PlnkService: + """PLNK服务类""" def __init__(self): - self.connections = {} # 存储连接 + self.device_manager = device_manager - async def _get_connection(self, device_id: str): - """获取连接对象""" - if device_id in self.connections: - return self.connections[device_id] - - # 从设备管理器获取连接信息 - device = await device_manager.get_device(device_id) - if not device: - raise ValueError(f"设备 {device_id} 不存在") - - connection_info = device.connection_info - connection_type = connection_info.get('connection_type', 'tcp') - - if connection_type == 'tcp': - return await self._get_tcp_connection(device_id, connection_info) - elif connection_type == 'serial': - return await self._get_serial_connection(device_id, connection_info) - else: - raise ValueError(f"不支持的连接类型: {connection_type}") - - async def _get_tcp_connection(self, device_id: str, connection_info: Dict[str, Any]): - """获取TCP连接""" + async def get_devices(self): + """获取所有PLNK设备""" try: - tcp_info = connection_info.get('tcp_info', {}) - host = tcp_info.get('host') - port = tcp_info.get('port') - timeout = tcp_info.get('timeout', 30) - - # 创建TCP连接 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(timeout) - sock.connect((host, port)) - - # 存储连接 - self.connections[device_id] = { - 'type': 'tcp', - 'socket': sock, - 'host': host, - 'port': port - } - - logger.info(f"TCP连接建立成功: {device_id} -> {host}:{port}") - return self.connections[device_id] - + devices = await self.device_manager.get_all_devices() + plnk_devices = [d for d in devices if d.protocol_type == "plnk"] + return plnk_devices except Exception as e: - logger.error(f"TCP连接失败: {device_id}, 错误: {e}") - raise ValueError(f"TCP连接失败: {str(e)}") + logger.error(f"获取PLNK设备失败: {e}") + return [] - async def _get_serial_connection(self, device_id: str, connection_info: Dict[str, Any]): - """获取串口连接""" + async def execute_command(self, device_id: str, command: str): + """在指定设备上执行PLNK命令""" try: - serial_info = connection_info.get('serial_info', {}) - port = serial_info.get('port') - baudrate = serial_info.get('baudrate', 115200) - timeout = serial_info.get('timeout', 5) + device = await self.device_manager.get_device(device_id) + if not device: + raise ValueError(f"设备 {device_id} 不存在") - # 创建串口连接 - ser = serial.Serial( - port=port, - baudrate=baudrate, - timeout=timeout - ) + if device.protocol_type != "plnk": + raise ValueError(f"设备 {device_id} 不是PLNK设备") - # 存储连接 - self.connections[device_id] = { - 'type': 'serial', - 'serial': ser, - 'port': port - } - - logger.info(f"串口连接建立成功: {device_id} -> {port}") - return self.connections[device_id] - - except Exception as e: - logger.error(f"串口连接失败: {device_id}, 错误: {e}") - raise ValueError(f"串口连接失败: {str(e)}") - - async def send_plnk_data(self, device_id: str, data: str, timeout: int = 30, - wait_response: bool = True) -> PLNKResponse: - """发送PLNK协议数据""" - start_time = time.time() - - try: - connection = await self._get_connection(device_id) - connection_type = connection['type'] + # 这里应该实现具体的PLNK命令执行逻辑 + logger.info(f"在设备 {device_id} 上执行PLNK命令: {command}") + return {"success": True, "command": command, "device_id": device_id} - # 将十六进制字符串转换为字节 - try: - data_bytes = bytes.fromhex(data.replace(' ', '')) - except ValueError as e: - raise ValueError(f"无效的十六进制数据: {data}") - - sent_data = data - received_data = "" - - if connection_type == 'tcp': - sock = connection['socket'] - - # 发送数据 - sock.send(data_bytes) - - if wait_response: - # 接收响应 - sock.settimeout(timeout) - response_bytes = sock.recv(1024) - received_data = response_bytes.hex().upper() - - elif connection_type == 'serial': - ser = connection['serial'] - - # 清空缓冲区 - ser.reset_input_buffer() - ser.reset_output_buffer() - - # 发送数据 - ser.write(data_bytes) - ser.flush() - - if wait_response: - # 接收响应 - start_timeout = time.time() - response_bytes = b"" - while time.time() - start_timeout < timeout: - if ser.in_waiting > 0: - response_bytes += ser.read(ser.in_waiting) - # 可以根据协议特点判断是否接收完整 - if len(response_bytes) > 0: - break - await asyncio.sleep(0.1) - - received_data = response_bytes.hex().upper() - - execution_time = time.time() - start_time - - # 解析响应数据(这里可以根据具体协议实现) - parsed_data = self._parse_plnk_response(received_data) - - success = len(received_data) > 0 - - logger.info(f"PLNK数据发送完成: {device_id} -> {data}, 成功: {success}") - - return PLNKResponse( - success=success, - sent_data=sent_data, - received_data=received_data, - parsed_data=parsed_data, - execution_time=execution_time - ) - - except Exception as e: - execution_time = time.time() - start_time - logger.error(f"PLNK数据发送失败: {device_id} -> {data}, 错误: {e}") - - return PLNKResponse( - success=False, - sent_data=data, - received_data="", - parsed_data=None, - execution_time=execution_time - ) - - def _parse_plnk_response(self, hex_data: str) -> Optional[Dict[str, Any]]: - """解析PLNK协议响应数据""" - if not hex_data: - return None - - try: - # 这里可以根据具体的PLNK协议格式进行解析 - # 示例解析格式(需要根据实际协议调整) - parsed = { - "raw_data": hex_data, - "length": len(hex_data) // 2, - "timestamp": time.time() - } - - # 如果数据长度足够,可以解析更多字段 - if len(hex_data) >= 4: - parsed["header"] = hex_data[:4] - parsed["payload"] = hex_data[4:-2] if len(hex_data) > 6 else hex_data[4:] - if len(hex_data) >= 6: - parsed["checksum"] = hex_data[-2:] - - return parsed - - except Exception as e: - logger.error(f"PLNK响应解析失败: {hex_data}, 错误: {e}") - return None - - async def test_connection(self, device_id: str) -> bool: - """测试连接""" - try: - connection = await self._get_connection(device_id) - - # 发送测试数据 - test_data = "0101" # 简单的测试数据 - result = await self.send_plnk_data(device_id, test_data, timeout=5) - - return result.success - - except Exception as e: - logger.error(f"PLNK连接测试失败: {device_id}, 错误: {e}") - return False - - async def close_connection(self, device_id: str) -> bool: - """关闭连接""" - try: - if device_id in self.connections: - connection = self.connections[device_id] - - if connection['type'] == 'tcp': - connection['socket'].close() - elif connection['type'] == 'serial': - connection['serial'].close() - - del self.connections[device_id] - logger.info(f"PLNK连接已关闭: {device_id}") - return True - return False - except Exception as e: - logger.error(f"关闭PLNK连接失败: {device_id}, 错误: {e}") - return False - - async def get_connection_status(self, device_id: str) -> Dict[str, Any]: - """获取连接状态""" - try: - if device_id in self.connections: - connection = self.connections[device_id] - return { - "device_id": device_id, - "connected": True, - "type": connection['type'], - "status": "online" - } - else: - return { - "device_id": device_id, - "connected": False, - "type": "unknown", - "status": "offline" - } except Exception as e: - logger.error(f"获取连接状态失败: {device_id}, 错误: {e}") - return { - "device_id": device_id, - "connected": False, - "type": "unknown", - "status": "error" - } \ No newline at end of file + logger.error(f"执行PLNK命令失败: {e}") + return {"success": False, "error": str(e)} \ No newline at end of file diff --git a/app/services/ssh_service.py b/app/services/ssh_service.py index ff562d1..a479f42 100644 --- a/app/services/ssh_service.py +++ b/app/services/ssh_service.py @@ -2,7 +2,7 @@ import asyncio import time from typing import Optional import paramiko -from app.core.device_manager import device_manager +from app.core.device.manager import device_manager from app.schemas.ssh import SSHExecRequest, SSHExecResponse, SSHConnectionInfo from app.utils.log import get_logger diff --git a/logs/app.core.device.manager.log b/logs/app.core.device.manager.log new file mode 100644 index 0000000..e69de29 diff --git a/modify.md b/modify.md index 9d11dc5..86c4a6a 100644 --- a/modify.md +++ b/modify.md @@ -376,4 +376,20 @@ logger.info("操作完成", device_count=5, duration_ms=1200) - 为团队成员提供统一的Git操作指南 - 规范代码提交流程,提高代码质量 - 减少Git操作错误,提高开发效率 -- 便于新成员快速上手Git工作流程 \ No newline at end of file +- 便于新成员快速上手Git工作流程 + +### 2024-07-09 - 设备管理器导入路径修复 + +- 将所有 `from app.core.device_manager import device_manager` 替换为 `from app.core.device.manager import device_manager` +- 涉及文件: + - app/services/adb_service.py + - app/services/at_service.py + - app/services/atx_service.py + - app/services/plnk_service.py + - app/services/ssh_service.py + - app/api/v1/endpoints/ssh.py + - app/api/v1/endpoints/plnk.py + - app/api/v1/endpoints/at.py + - app/api/v1/endpoints/adb.py + - app/api/v1/endpoints/devices.py +- 目的:适配设备管理器模块重构后的新路径,解决模块导入错误。 \ No newline at end of file diff --git a/test_config_only.py b/test_config_only.py new file mode 100644 index 0000000..30de391 --- /dev/null +++ b/test_config_only.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +""" +只测试配置模块的导入 +""" +import sys +import os + +# 添加项目根目录到Python路径 +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +try: + # 直接导入settings模块 + from app.core.config.settings import ProtocolType, DeviceStatus + print("✅ 直接导入settings成功!") + print(f"ProtocolType.ADB: {ProtocolType.ADB}") + print(f"DeviceStatus.ONLINE: {DeviceStatus.ONLINE}") + + # 测试从config模块导入 + from app.core.config import ProtocolType as PT, DeviceStatus as DS + print("✅ 从config模块导入成功!") + print(f"PT.ADB: {PT.ADB}") + print(f"DS.ONLINE: {DS.ONLINE}") + +except ImportError as e: + print(f"❌ 导入失败: {e}") +except Exception as e: + print(f"❌ 其他错误: {e}") + import traceback + traceback.print_exc() \ No newline at end of file diff --git a/test_import.py b/test_import.py new file mode 100644 index 0000000..c5afe9f --- /dev/null +++ b/test_import.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +""" +测试导入脚本 +""" +try: + from app.core.config import ProtocolType, DeviceStatus + print("✅ 导入成功!") + print(f"ProtocolType.ADB: {ProtocolType.ADB}") + print(f"DeviceStatus.ONLINE: {DeviceStatus.ONLINE}") + print(f"DeviceStatus.OFFLINE: {DeviceStatus.OFFLINE}") +except ImportError as e: + print(f"❌ 导入失败: {e}") +except Exception as e: + print(f"❌ 其他错误: {e}") \ No newline at end of file