-
-
Notifications
You must be signed in to change notification settings - Fork 668
Expand file tree
/
Copy pathmain.py
More file actions
159 lines (132 loc) · 5.22 KB
/
main.py
File metadata and controls
159 lines (132 loc) · 5.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import argparse
import asyncio
import signal
import sys
from src.application import Application
from src.utils.logging_config import get_logger, setup_logging
logger = get_logger(__name__)
def parse_args():
"""
解析命令行参数.
"""
parser = argparse.ArgumentParser(description="小智Ai客户端")
parser.add_argument(
"--mode",
choices=["gui", "cli"],
default="gui",
help="运行模式:gui(图形界面) 或 cli(命令行)",
)
parser.add_argument(
"--protocol",
choices=["mqtt", "websocket"],
default="websocket",
help="通信协议:mqtt 或 websocket",
)
parser.add_argument(
"--skip-activation",
action="store_true",
help="跳过激活流程,直接启动应用(仅用于调试)",
)
return parser.parse_args()
async def handle_activation(mode: str) -> bool:
"""处理设备激活流程,依赖已有事件循环.
Args:
mode: 运行模式,"gui"或"cli"
Returns:
bool: 激活是否成功
"""
try:
from src.core.system_initializer import SystemInitializer
logger.info("开始设备激活流程检查...")
system_initializer = SystemInitializer()
# 统一使用 SystemInitializer 内的激活处理,GUI/CLI 自适应
result = await system_initializer.handle_activation_process(mode=mode)
success = bool(result.get("is_activated", False))
logger.info(f"激活流程完成,结果: {success}")
return success
except Exception as e:
logger.error(f"激活流程异常: {e}", exc_info=True)
return False
async def start_app(mode: str, protocol: str, skip_activation: bool) -> int:
"""
启动应用的统一入口(在已有事件循环中执行).
"""
logger.info("启动小智AI客户端")
# 处理激活流程
if not skip_activation:
activation_success = await handle_activation(mode)
if not activation_success:
logger.error("设备激活失败,程序退出")
return 1
else:
logger.warning("跳过激活流程(调试模式)")
# 创建并启动应用程序
app = Application.get_instance()
return await app.run(mode=mode, protocol=protocol)
if __name__ == "__main__":
exit_code = 1
try:
args = parse_args()
setup_logging()
# 检测Wayland环境并设置Qt平台插件配置
import os
is_wayland = (
os.environ.get("WAYLAND_DISPLAY")
or os.environ.get("XDG_SESSION_TYPE") == "wayland"
)
if args.mode == "gui" and is_wayland:
# 在Wayland环境下,确保Qt使用正确的平台插件
if "QT_QPA_PLATFORM" not in os.environ:
# 优先使用wayland插件,失败则回退到xcb(X11兼容层)
os.environ["QT_QPA_PLATFORM"] = "wayland;xcb"
logger.info("Wayland环境:设置QT_QPA_PLATFORM=wayland;xcb")
# 禁用一些在Wayland下不稳定的Qt特性
os.environ.setdefault("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1")
logger.info("Wayland环境检测完成,已应用兼容性配置")
# 统一设置信号处理:忽略 macOS 上可能出现的 SIGTRAP,避免“trace trap”导致进程退出
try:
if hasattr(signal, "SIGINT"):
# 交由 qasync/Qt 处理 Ctrl+C;保持默认或后续由 GUI 层处理
pass
if hasattr(signal, "SIGTERM"):
# 允许进程收到终止信号时走正常关闭路径
pass
if hasattr(signal, "SIGTRAP"):
signal.signal(signal.SIGTRAP, signal.SIG_IGN)
except Exception:
# 某些平台/环境不支持设置这些信号,忽略即可
pass
if args.mode == "gui":
# 在GUI模式下,由main统一创建 QApplication 与 qasync 事件循环
try:
import qasync
from PyQt5.QtWidgets import QApplication
except ImportError as e:
logger.error(f"GUI模式需要qasync和PyQt5库: {e}")
sys.exit(1)
qt_app = QApplication.instance() or QApplication(sys.argv)
loop = qasync.QEventLoop(qt_app)
asyncio.set_event_loop(loop)
logger.info("已在main中创建qasync事件循环")
# 确保关闭最后一个窗口不会自动退出应用,避免事件环提前停止
try:
qt_app.setQuitOnLastWindowClosed(False)
except Exception:
pass
with loop:
exit_code = loop.run_until_complete(
start_app(args.mode, args.protocol, args.skip_activation)
)
else:
# CLI模式使用标准asyncio事件循环
exit_code = asyncio.run(
start_app(args.mode, args.protocol, args.skip_activation)
)
except KeyboardInterrupt:
logger.info("程序被用户中断")
exit_code = 0
except Exception as e:
logger.error(f"程序异常退出: {e}", exc_info=True)
exit_code = 1
finally:
sys.exit(exit_code)