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 c581454..2606d6a 100644 Binary files a/app/core/__pycache__/__init__.cpython-310.pyc and b/app/core/__pycache__/__init__.cpython-310.pyc differ 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 0000000..f09fe02 Binary files /dev/null and b/app/core/adb/__pycache__/connection.cpython-310.pyc differ 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 96bbc54..7a574be 100644 Binary files a/app/core/config/__pycache__/__init__.cpython-310.pyc and b/app/core/config/__pycache__/__init__.cpython-310.pyc differ 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 0000000..7332fbc Binary files /dev/null and b/app/core/device/__pycache__/__init__.cpython-310.pyc differ 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 0000000..34fac39 Binary files /dev/null and b/app/core/device/__pycache__/dispatcher.cpython-310.pyc differ diff --git a/app/core/device/__pycache__/manager.cpython-310.pyc b/app/core/device/__pycache__/manager.cpython-310.pyc new file mode 100644 index 0000000..8b69a7f Binary files /dev/null and b/app/core/device/__pycache__/manager.cpython-310.pyc differ diff --git a/app/core/handlers/__pycache__/exception_handlers.cpython-310.pyc b/app/core/handlers/__pycache__/exception_handlers.cpython-310.pyc index 1bcc591..886fb1d 100644 Binary files a/app/core/handlers/__pycache__/exception_handlers.cpython-310.pyc and b/app/core/handlers/__pycache__/exception_handlers.cpython-310.pyc differ diff --git a/app/schemas/__pycache__/__init__.cpython-310.pyc b/app/schemas/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..30b4b87 Binary files /dev/null and b/app/schemas/__pycache__/__init__.cpython-310.pyc differ diff --git a/app/schemas/__pycache__/device.cpython-310.pyc b/app/schemas/__pycache__/device.cpython-310.pyc new file mode 100644 index 0000000..7feaa4c Binary files /dev/null and b/app/schemas/__pycache__/device.cpython-310.pyc differ diff --git a/app/services/__pycache__/__init__.cpython-310.pyc b/app/services/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..306c14b Binary files /dev/null and b/app/services/__pycache__/__init__.cpython-310.pyc differ diff --git a/app/services/__pycache__/adb_service.cpython-310.pyc b/app/services/__pycache__/adb_service.cpython-310.pyc new file mode 100644 index 0000000..5e2625b Binary files /dev/null and b/app/services/__pycache__/adb_service.cpython-310.pyc differ 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