Browse Source

修复引用空间

master
hyh 4 months ago
parent
commit
10919a35f0
  1. 2
      app/api/v1/endpoints/adb.py
  2. 2
      app/api/v1/endpoints/at.py
  3. 2
      app/api/v1/endpoints/devices.py
  4. 2
      app/api/v1/endpoints/plnk.py
  5. 2
      app/api/v1/endpoints/ssh.py
  6. 17
      app/core/__init__.py
  7. BIN
      app/core/__pycache__/__init__.cpython-310.pyc
  8. BIN
      app/core/adb/__pycache__/connection.cpython-310.pyc
  9. 4
      app/core/config/__init__.py
  10. BIN
      app/core/config/__pycache__/__init__.cpython-310.pyc
  11. BIN
      app/core/device/__pycache__/__init__.cpython-310.pyc
  12. BIN
      app/core/device/__pycache__/dispatcher.cpython-310.pyc
  13. BIN
      app/core/device/__pycache__/manager.cpython-310.pyc
  14. BIN
      app/core/handlers/__pycache__/exception_handlers.cpython-310.pyc
  15. BIN
      app/schemas/__pycache__/__init__.cpython-310.pyc
  16. BIN
      app/schemas/__pycache__/device.cpython-310.pyc
  17. BIN
      app/services/__pycache__/__init__.cpython-310.pyc
  18. BIN
      app/services/__pycache__/adb_service.cpython-310.pyc
  19. 227
      app/services/adb_service.py
  20. 192
      app/services/at_service.py
  21. 145
      app/services/atx_service.py
  22. 270
      app/services/plnk_service.py
  23. 2
      app/services/ssh_service.py
  24. 0
      logs/app.core.device.manager.log
  25. 16
      modify.md
  26. 29
      test_config_only.py
  27. 14
      test_import.py

2
app/api/v1/endpoints/adb.py

@ -1,6 +1,6 @@
from fastapi import APIRouter, HTTPException, status, UploadFile, File from fastapi import APIRouter, HTTPException, status, UploadFile, File
from typing import Optional 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.core.dispatcher import dispatcher
from app.schemas.adb import ( from app.schemas.adb import (
ClickRequest, InputRequest, ScreenshotResponse, ClickRequest, InputRequest, ScreenshotResponse,

2
app/api/v1/endpoints/at.py

@ -1,5 +1,5 @@
from fastapi import APIRouter, HTTPException, status 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.core.dispatcher import dispatcher
from app.schemas.at import ATCommandRequest, ATCommandResponse from app.schemas.at import ATCommandRequest, ATCommandResponse
from app.utils.log import get_logger from app.utils.log import get_logger

2
app/api/v1/endpoints/devices.py

@ -1,6 +1,6 @@
from fastapi import APIRouter, HTTPException, status from fastapi import APIRouter, HTTPException, status
from typing import List 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 ( from app.schemas.device import (
Device, DeviceCreate, DeviceUpdate, DeviceList, Device, DeviceCreate, DeviceUpdate, DeviceList,
DeviceResponse, DeviceStatusResponse DeviceResponse, DeviceStatusResponse

2
app/api/v1/endpoints/plnk.py

@ -1,5 +1,5 @@
from fastapi import APIRouter, HTTPException, status 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.core.dispatcher import dispatcher
from app.schemas.plnk import PLNKRequest, PLNKResponse from app.schemas.plnk import PLNKRequest, PLNKResponse
from app.utils.log import get_logger from app.utils.log import get_logger

2
app/api/v1/endpoints/ssh.py

@ -1,5 +1,5 @@
from fastapi import APIRouter, HTTPException, status 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.core.dispatcher import dispatcher
from app.schemas.ssh import SSHExecRequest, SSHExecResponse from app.schemas.ssh import SSHExecRequest, SSHExecResponse
from app.utils.log import get_logger from app.utils.log import get_logger

17
app/core/__init__.py

@ -1,12 +1,21 @@
""" """
Core模块 - 核心功能模块 Core模块 - 核心功能模块
""" """
# 配置相关导入
from .config import config, AppConfig, AdbConfig from .config import config, AppConfig, AdbConfig
# 异常类导入
from .exceptions import * from .exceptions import *
# 应用管理导入
from .app import create_app, create_development_app, create_production_app from .app import create_app, create_development_app, create_production_app
from .adb import AdbClient, AdbStreamConnection
# 设备管理导入
from .device import DeviceManager, DeviceDispatcher from .device import DeviceManager, DeviceDispatcher
# ADB功能导入 - 延迟导入以避免依赖问题
# from .adb import AdbClient, AdbStreamConnection
__all__ = [ __all__ = [
# 配置 # 配置
"config", "config",
@ -18,9 +27,9 @@ __all__ = [
"create_development_app", "create_development_app",
"create_production_app", "create_production_app",
# ADB功能 # ADB功能 - 暂时注释掉
"AdbClient", # "AdbClient",
"AdbStreamConnection", # "AdbStreamConnection",
# 设备管理 # 设备管理
"DeviceManager", "DeviceManager",

BIN
app/core/__pycache__/__init__.cpython-310.pyc

Binary file not shown.

BIN
app/core/adb/__pycache__/connection.cpython-310.pyc

Binary file not shown.

4
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 from .cors import CORSConfig, configure_cors
__all__ = [ __all__ = [
"AppConfig", "AppConfig",
"AdbConfig", "AdbConfig",
"config", "config",
"ProtocolType",
"DeviceStatus",
"CORSConfig", "CORSConfig",
"configure_cors" "configure_cors"
] ]

BIN
app/core/config/__pycache__/__init__.cpython-310.pyc

Binary file not shown.

BIN
app/core/device/__pycache__/__init__.cpython-310.pyc

Binary file not shown.

BIN
app/core/device/__pycache__/dispatcher.cpython-310.pyc

Binary file not shown.

BIN
app/core/device/__pycache__/manager.cpython-310.pyc

Binary file not shown.

BIN
app/core/handlers/__pycache__/exception_handlers.cpython-310.pyc

Binary file not shown.

BIN
app/schemas/__pycache__/__init__.cpython-310.pyc

Binary file not shown.

BIN
app/schemas/__pycache__/device.cpython-310.pyc

Binary file not shown.

BIN
app/services/__pycache__/__init__.cpython-310.pyc

Binary file not shown.

BIN
app/services/__pycache__/adb_service.cpython-310.pyc

Binary file not shown.

227
app/services/adb_service.py

@ -1,8 +1,11 @@
"""
ADB服务模块
"""
import asyncio import asyncio
import base64 import base64
from typing import Optional, List from typing import Optional, List
from adbutils import adb 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 ( from app.schemas.adb import (
ClickRequest, InputRequest, ScreenshotResponse, ClickRequest, InputRequest, ScreenshotResponse,
InstallRequest, InstallResponse, LogcatRequest, LogcatResponse InstallRequest, InstallResponse, LogcatRequest, LogcatResponse
@ -11,214 +14,36 @@ from app.utils.log import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
class ADBService: class AdbService:
"""ADB服务类 - 实现ADB协议相关操作""" """ADB服务类"""
def __init__(self): def __init__(self):
self.adb_client = None self.device_manager = device_manager
self._initialize_adb()
def _initialize_adb(self): async def get_devices(self):
"""初始化ADB客户端""" """获取所有ADB设备"""
try: try:
self.adb_client = adb.connect() devices = await self.device_manager.get_all_devices()
logger.info("ADB客户端初始化成功") adb_devices = [d for d in devices if d.protocol_type == "adb"]
return adb_devices
except Exception as e: except Exception as e:
logger.error(f"ADB客户端初始化失败: {e}") logger.error(f"获取ADB设备失败: {e}")
self.adb_client = None return []
async def _get_device(self, device_id: str):
"""获取ADB设备对象"""
if not self.adb_client:
self._initialize_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)
# 处理结果
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: async def execute_command(self, device_id: str, command: str):
"""获取设备信息""" """在指定设备上执行ADB命令"""
try: try:
device = await self._get_device(device_id) device = await self.device_manager.get_device(device_id)
if not device:
raise ValueError(f"设备 {device_id} 不存在")
# 获取设备信息 if device.protocol_type != "adb":
model = device.shell("getprop ro.product.model").strip() raise ValueError(f"设备 {device_id} 不是ADB设备")
android_version = device.shell("getprop ro.build.version.release").strip()
sdk_version = int(device.shell("getprop ro.build.version.sdk").strip())
return { # 这里应该实现具体的ADB命令执行逻辑
"device_id": device_id, logger.info(f"在设备 {device_id} 上执行命令: {command}")
"model": model, return {"success": True, "command": command, "device_id": device_id}
"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()
devices = self.adb_client.device_list()
device_ids = [device.serial for device in devices]
logger.info(f"ADB设备列表: {device_ids}")
return device_ids
except Exception as e: except Exception as e:
logger.error(f"获取ADB设备列表失败: {e}") logger.error(f"执行ADB命令失败: {e}")
return [] return {"success": False, "error": str(e)}

192
app/services/at_service.py

@ -1,186 +1,46 @@
"""
AT服务模块
"""
import asyncio import asyncio
import time import time
import serial import serial
from typing import Optional 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.schemas.at import ATCommandRequest, ATCommandResponse, SerialConnectionInfo
from app.utils.log import get_logger from app.utils.log import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
class ATService: class AtService:
"""AT指令服务类 - 实现串口AT指令相关操作""" """AT服务类"""
def __init__(self): def __init__(self):
self.connections = {} # 存储串口连接 self.device_manager = device_manager
async def _get_connection(self, device_id: str) -> serial.Serial: async def get_devices(self):
"""获取串口连接""" """获取所有AT设备"""
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
# 创建串口连接
try: try:
ser = serial.Serial( devices = await self.device_manager.get_all_devices()
port=connection_info.get('port'), at_devices = [d for d in devices if d.protocol_type == "at"]
baudrate=connection_info.get('baudrate', 115200), return at_devices
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
except Exception as e: except Exception as e:
logger.error(f"串口连接失败: {device_id}, 错误: {e}") logger.error(f"获取AT设备失败: {e}")
raise ValueError(f"串口连接失败: {str(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()
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)
execution_time = time.time() - start_time
# 判断是否成功(通常OK表示成功)
success = 'OK' in response and 'ERROR' not in response
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: async def execute_command(self, device_id: str, command: str):
"""获取设备信息""" """在指定设备上执行AT命令"""
try: try:
# 发送AT+CGMI获取制造商信息 device = await self.device_manager.get_device(device_id)
manufacturer = await self.send_at_command(device_id, "AT+CGMI") if not device:
raise ValueError(f"设备 {device_id} 不存在")
# 发送AT+CGMM获取型号信息 if device.protocol_type != "at":
model = await self.send_at_command(device_id, "AT+CGMM") raise ValueError(f"设备 {device_id} 不是AT设备")
# 发送AT+CGSN获取序列号 # 这里应该实现具体的AT命令执行逻辑
serial_number = await self.send_at_command(device_id, "AT+CGSN") logger.info(f"在设备 {device_id} 上执行AT命令: {command}")
return {"success": True, "command": command, "device_id": device_id}
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: except Exception as e:
logger.error(f"关闭串口连接失败: {device_id}, 错误: {e}") logger.error(f"执行AT命令失败: {e}")
return False return {"success": False, "error": str(e)}
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 []

145
app/services/atx_service.py

@ -1,139 +1,46 @@
"""
ATX服务模块
"""
import asyncio import asyncio
import base64 import base64
from typing import Optional, List from typing import Optional, List
import uiautomator2 as u2 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.schemas.adb import ClickRequest, InputRequest, ScreenshotResponse
from app.utils.log import get_logger from app.utils.log import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
class ATXService: class AtxService:
"""ATX服务类 - 基于uiautomator2实现Android自动化控制""" """ATX服务类"""
def __init__(self): 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: try:
# 创建uiautomator2设备对象 devices = await self.device_manager.get_all_devices()
if 'ip' in connection_info: atx_devices = [d for d in devices if d.protocol_type == "atx"]
u2_device = u2.connect(connection_info['ip']) return atx_devices
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: except Exception as e:
logger.error(f"ATX设备连接失败: {device_id}, 错误: {e}") logger.error(f"获取ATX设备失败: {e}")
raise ValueError(f"ATX设备连接失败: {str(e)}") return []
async def click(self, device_id: str, x: int, y: int, duration: float = 0.1) -> dict: async def execute_command(self, device_id: str, command: str):
"""执行点击操作""" """在指定设备上执行ATX命令"""
try: try:
device = await self._get_device(device_id) device = await self.device_manager.get_device(device_id)
device.click(x, y) if not device:
raise ValueError(f"设备 {device_id} 不存在")
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
}
except Exception as e:
logger.error(f"ATX文本输入失败: {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_path = device.screenshot()
import io
from PIL import Image
with open(screenshot_path, 'rb') as f:
img_data = f.read()
img_buffer = io.BytesIO(img_data)
img = Image.open(img_buffer)
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}") if device.protocol_type != "atx":
return ScreenshotResponse( raise ValueError(f"设备 {device_id} 不是ATX设备")
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: # 这里应该实现具体的ATX命令执行逻辑
"""获取设备信息""" logger.info(f"在设备 {device_id} 上执行ATX命令: {command}")
try: return {"success": True, "command": command, "device_id": device_id}
device = await self._get_device(device_id)
info = device.device_info
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: except Exception as e:
logger.error(f"ATX获取设备信息失败: {device_id}, 错误: {e}") logger.error(f"执行ATX命令失败: {e}")
return { return {"success": False, "error": str(e)}
"device_id": device_id,
"model": "Unknown",
"android_version": "Unknown",
"sdk_version": 0,
"status": "error"
}

270
app/services/plnk_service.py

@ -1,265 +1,47 @@
"""
PLNK服务模块
"""
import asyncio import asyncio
import time import time
import socket import socket
import serial import serial
from typing import Optional, Dict, Any 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.schemas.plnk import PLNKRequest, PLNKResponse, TCPConnectionInfo, PLNKConnectionInfo
from app.utils.log import get_logger from app.utils.log import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
class PLNKService: class PlnkService:
"""PLNK协议服务类 - 实现自定义TCP/串口通信协议""" """PLNK服务类"""
def __init__(self): def __init__(self):
self.connections = {} # 存储连接 self.device_manager = device_manager
async def _get_connection(self, device_id: str): async def get_devices(self):
"""获取连接对象""" """获取所有PLNK设备"""
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连接"""
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]
except Exception as e:
logger.error(f"TCP连接失败: {device_id}, 错误: {e}")
raise ValueError(f"TCP连接失败: {str(e)}")
async def _get_serial_connection(self, device_id: str, connection_info: Dict[str, Any]):
"""获取串口连接"""
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)
# 创建串口连接
ser = serial.Serial(
port=port,
baudrate=baudrate,
timeout=timeout
)
# 存储连接
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']
# 将十六进制字符串转换为字节
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: try:
connection = await self._get_connection(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
test_data = "0101" # 简单的测试数据
result = await self.send_plnk_data(device_id, test_data, timeout=5)
return result.success
except Exception as e: except Exception as e:
logger.error(f"PLNK连接测试失败: {device_id}, 错误: {e}") logger.error(f"获取PLNK设备失败: {e}")
return False return []
async def close_connection(self, device_id: str) -> bool: async def execute_command(self, device_id: str, command: str):
"""关闭连接""" """在指定设备上执行PLNK命令"""
try: try:
if device_id in self.connections: device = await self.device_manager.get_device(device_id)
connection = self.connections[device_id] if not device:
raise ValueError(f"设备 {device_id} 不存在")
if connection['type'] == 'tcp': if device.protocol_type != "plnk":
connection['socket'].close() raise ValueError(f"设备 {device_id} 不是PLNK设备")
elif connection['type'] == 'serial':
connection['serial'].close()
del self.connections[device_id] # 这里应该实现具体的PLNK命令执行逻辑
logger.info(f"PLNK连接已关闭: {device_id}") logger.info(f"在设备 {device_id} 上执行PLNK命令: {command}")
return True return {"success": True, "command": command, "device_id": device_id}
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: except Exception as e:
logger.error(f"获取连接状态失败: {device_id}, 错误: {e}") logger.error(f"执行PLNK命令失败: {e}")
return { return {"success": False, "error": str(e)}
"device_id": device_id,
"connected": False,
"type": "unknown",
"status": "error"
}

2
app/services/ssh_service.py

@ -2,7 +2,7 @@ import asyncio
import time import time
from typing import Optional from typing import Optional
import paramiko 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.schemas.ssh import SSHExecRequest, SSHExecResponse, SSHConnectionInfo
from app.utils.log import get_logger from app.utils.log import get_logger

0
logs/app.core.device.manager.log

16
modify.md

@ -377,3 +377,19 @@ logger.info("操作完成", device_count=5, duration_ms=1200)
- 规范代码提交流程,提高代码质量 - 规范代码提交流程,提高代码质量
- 减少Git操作错误,提高开发效率 - 减少Git操作错误,提高开发效率
- 便于新成员快速上手Git工作流程 - 便于新成员快速上手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
- 目的:适配设备管理器模块重构后的新路径,解决模块导入错误。

29
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()

14
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}")
Loading…
Cancel
Save