Files
AUTO-MAS-test/app/utils/device_manager/mumu.py
2025-09-27 03:32:43 +08:00

221 lines
7.2 KiB
Python

import asyncio
import json
from app.utils.device_manager.utils import BaseDevice, ExeRunner, DeviceStatus
from app.utils.logger import get_logger
class MumuManager(BaseDevice):
"""
基于MuMuManager.exe的模拟器管理
"""
def __init__(self, exe_path: str) -> None:
"""_summary_
Args:
exe_path (str): MuMuManager.exe的绝对路径
"""
self.runner = ExeRunner(exe_path, "utf-8")
self.logger = get_logger("MuMu管理器")
self.wait_time = 60 # 配置获取 后续改一下 单位为s
async def start(self, idx: str, package_name="") -> tuple[bool, int, dict]:
"""
启动指定模拟器
Returns:
tuple[bool, int, str]: 是否成功, 当前状态码, ADB端口信息
"""
status = await self.get_status(idx)
if status != DeviceStatus.OFFLINE:
self.logger.error(
f"模拟器{idx}未处于关闭状态,当前状态码: {status}, 需求状态码: {DeviceStatus.OFFLINE}"
)
return False, status, {}
if package_name:
result = self.runner.run(
"control",
"-v",
idx,
"launch",
"-pkg",
package_name,
)
else:
result = self.runner.run(
"control",
"-v",
idx,
"launch",
)
# 参考命令 MuMuManager.exe control -v 2 launch
self.logger.debug(f"启动结果:{result}")
if result.returncode != 0:
raise RuntimeError(f"命令执行失败: {result}")
i = 0
while i < self.wait_time * 10:
status = await self.get_status(idx)
if status == DeviceStatus.ERROR or status == DeviceStatus.UNKNOWN:
self.logger.error(f"模拟器{idx}启动失败,状态码: {status}")
return False, status, {}
if status == DeviceStatus.ONLINE:
OK, info = await self.get_device_info(idx)
self.logger.debug(info)
if OK:
data = json.loads(info)
adb_port = data.get("adb_port")
adb_host_ip = data.get("adb_host_ip")
if adb_port and adb_host_ip:
return (
True,
status,
{"adb_port": adb_port, "adb_host_ip": adb_host_ip},
)
return True, status, {}
await asyncio.sleep(0.1)
i += 1
return False, DeviceStatus.UNKNOWN, {}
async def close(self, idx: str) -> tuple[bool, int]:
"""
关闭指定模拟器
Returns:
tuple[bool, int]: 是否成功, 当前状态码
"""
status = await self.get_status(idx)
if status != DeviceStatus.ONLINE and status != DeviceStatus.STARTING:
return False, DeviceStatus.NOT_FOUND
result = self.runner.run(
"control",
"-v",
idx,
"shutdown",
)
# 参考命令 MuMuManager.exe control -v 2 shutdown
if result.returncode != 0:
return True, DeviceStatus.OFFLINE
i = 0
while i < self.wait_time * 10:
status = await self.get_status(idx)
if status == DeviceStatus.ERROR or status == DeviceStatus.UNKNOWN:
return False, status
if status == DeviceStatus.OFFLINE:
return True, DeviceStatus.OFFLINE
await asyncio.sleep(0.1)
i += 1
return False, DeviceStatus.UNKNOWN
async def get_status(self, idx: str, data: str | None = None) -> int:
if not data:
OK, result_str = await self.get_device_info(idx)
self.logger.debug(f"获取状态结果{result_str}")
else:
OK, result_str = True, data
try:
result_json = json.loads(result_str)
if OK:
if result_json["is_android_started"]:
return DeviceStatus.STARTING
elif result_json["is_process_started"]:
return DeviceStatus.ONLINE
else:
return DeviceStatus.OFFLINE
else:
if result_json["errmsg"] == "unknown error":
return DeviceStatus.UNKNOWN
else:
return DeviceStatus.ERROR
except json.JSONDecodeError as e:
self.logger.error(f"JSON解析错误: {e}")
return DeviceStatus.UNKNOWN
async def get_device_info(self, idx: str) -> tuple[bool, str]:
result = self.runner.run(
"info",
"-v",
idx,
)
self.logger.debug(f"获取模拟器{idx}信息: {result}")
if result.returncode != 0:
return False, result.stdout.strip()
else:
return True, result.stdout.strip()
async def _get_all_info(self) -> str:
result = self.runner.run(
"info",
"-v",
"all",
)
# self.logger.debug(f"result{result.stdout.strip()}")
if result.returncode != 0:
raise RuntimeError(f"命令执行失败: {result}")
return result.stdout.strip()
async def get_all_info(self) -> dict[str, dict[str, str]]:
json_data = await self._get_all_info()
data = json.loads(json_data)
result: dict[str, dict[str, str]] = {}
if not data:
return result
if isinstance(data, dict) and "index" in data and "name" in data:
index = data["index"]
name = data["name"]
status = self.get_status(index, json_data)
result[index] = {
"title": name,
"status": str(status),
}
elif isinstance(data, dict):
for key, value in data.items():
if isinstance(value, dict) and "index" in value and "name" in value:
index = value["index"]
name = value["name"]
status = await self.get_status(index)
result[index] = {
"title": name,
"status": str(status),
}
return result
async def hide_device(self, idx: str) -> tuple[bool, int]:
"""隐藏设备窗口"""
status = await self.get_status(idx)
if status != DeviceStatus.ONLINE:
return False, status
result = self.runner.run(
"control",
"-v",
idx,
"hide_window",
)
if result.returncode != 0:
return False, status
return True, DeviceStatus.ONLINE
async def show_device(self, idx: str) -> tuple[bool, int]:
"""显示设备窗口"""
status = await self.get_status(idx)
if status != DeviceStatus.ONLINE:
return False, status
result = self.runner.run(
"control",
"-v",
idx,
"show_window",
)
if result.returncode != 0:
return False, status
return True, DeviceStatus.ONLINE