You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
7.0 KiB
186 lines
7.0 KiB
|
4 months ago
|
import asyncio
|
||
|
|
import time
|
||
|
|
import serial
|
||
|
|
from typing import Optional
|
||
|
|
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指令相关操作"""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.connections = {} # 存储串口连接
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
# 创建串口连接
|
||
|
|
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
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"串口连接失败: {device_id}, 错误: {e}")
|
||
|
|
raise ValueError(f"串口连接失败: {str(e)}")
|
||
|
|
|
||
|
|
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:
|
||
|
|
"""获取设备信息"""
|
||
|
|
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 []
|