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. 18
      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 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,

2
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

2
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

2
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

2
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

17
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",

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
__all__ = [
"AppConfig",
"AdbConfig",
"config",
"ProtocolType",
"DeviceStatus",
"CORSConfig",
"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 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 []
logger.error(f"执行ADB命令失败: {e}")
return {"success": False, "error": str(e)}

192
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 []
logger.error(f"执行AT命令失败: {e}")
return {"success": False, "error": str(e)}

145
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"
}
logger.error(f"执行ATX命令失败: {e}")
return {"success": False, "error": str(e)}

270
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"
}
logger.error(f"执行PLNK命令失败: {e}")
return {"success": False, "error": str(e)}

2
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

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

18
modify.md

@ -376,4 +376,20 @@ 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