mqttxx 2.0.3__py3-none-any.whl → 3.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mqttxx/__init__.py +13 -5
- mqttxx/client.py +295 -150
- mqttxx/config.py +14 -0
- mqttxx/conventions.py +8 -5
- mqttxx/events.py +340 -0
- mqttxx/protocol.py +189 -14
- mqttxx/rpc.py +46 -6
- mqttxx-3.1.2.dist-info/METADATA +910 -0
- mqttxx-3.1.2.dist-info/RECORD +13 -0
- mqttxx-2.0.3.dist-info/METADATA +0 -490
- mqttxx-2.0.3.dist-info/RECORD +0 -12
- {mqttxx-2.0.3.dist-info → mqttxx-3.1.2.dist-info}/LICENSE +0 -0
- {mqttxx-2.0.3.dist-info → mqttxx-3.1.2.dist-info}/WHEEL +0 -0
- {mqttxx-2.0.3.dist-info → mqttxx-3.1.2.dist-info}/top_level.txt +0 -0
mqttxx/rpc.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# MQTT RPC 模块 - 基于 aiomqtt 的双向对等 RPC 调用
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import json
|
|
5
4
|
import uuid
|
|
6
|
-
from typing import Any, Callable, Optional
|
|
5
|
+
from typing import Any, Callable, Optional, Type
|
|
7
6
|
from loguru import logger
|
|
8
7
|
|
|
9
8
|
from .client import MQTTClient
|
|
@@ -14,8 +13,9 @@ from .exceptions import (
|
|
|
14
13
|
TooManyConcurrentCallsError,
|
|
15
14
|
MQTTXError,
|
|
16
15
|
ErrorCode,
|
|
16
|
+
MessageError,
|
|
17
17
|
)
|
|
18
|
-
from .protocol import RPCRequest, RPCResponse
|
|
18
|
+
from .protocol import RPCRequest, RPCResponse, parse_message_from_bytes, Codec, JSONCodec
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
# 权限检查回调类型
|
|
@@ -76,6 +76,7 @@ class RPCManager:
|
|
|
76
76
|
client: MQTTClient,
|
|
77
77
|
config: Optional[RPCConfig] = None,
|
|
78
78
|
auth_callback: Optional[AuthCallback] = None,
|
|
79
|
+
codec: Type[Codec] = JSONCodec, # 新增:可插拔编解码器
|
|
79
80
|
):
|
|
80
81
|
"""初始化 RPC 管理器
|
|
81
82
|
|
|
@@ -85,6 +86,7 @@ class RPCManager:
|
|
|
85
86
|
auth_callback: 权限检查回调函数(可选)
|
|
86
87
|
签名:async def auth_callback(caller_id: str, method: str, request: RPCRequest) -> bool
|
|
87
88
|
返回:True = 允许,False = 拒绝
|
|
89
|
+
codec: 编解码器(默认 JSONCodec)
|
|
88
90
|
|
|
89
91
|
使用示例:
|
|
90
92
|
client = MQTTClient(...)
|
|
@@ -92,6 +94,7 @@ class RPCManager:
|
|
|
92
94
|
|
|
93
95
|
# 基础用法(无权限控制)
|
|
94
96
|
rpc = RPCManager(client)
|
|
97
|
+
rpc.setup("my/rpc/responses") # 设置响应主题
|
|
95
98
|
|
|
96
99
|
# 带权限控制
|
|
97
100
|
async def auth_check(caller_id, method, request):
|
|
@@ -102,6 +105,7 @@ class RPCManager:
|
|
|
102
105
|
self._client = client
|
|
103
106
|
self.config = config or RPCConfig()
|
|
104
107
|
self._auth_callback = auth_callback
|
|
108
|
+
self._codec = codec # 保存编解码器
|
|
105
109
|
|
|
106
110
|
# RPC 状态
|
|
107
111
|
self._pending_calls: dict[str, asyncio.Future] = {} # request_id → Future
|
|
@@ -112,6 +116,40 @@ class RPCManager:
|
|
|
112
116
|
|
|
113
117
|
logger.info("RPCManager 已初始化")
|
|
114
118
|
|
|
119
|
+
def setup(self, reply_topic: str):
|
|
120
|
+
"""设置 RPC 响应主题并自动订阅
|
|
121
|
+
|
|
122
|
+
这个方法会:
|
|
123
|
+
1. 订阅 reply_topic(接收 RPC 响应)
|
|
124
|
+
2. 注册消息处理器(自动解码 + 分发)
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
reply_topic: 响应主题(例如:server/rpc_responses)
|
|
128
|
+
|
|
129
|
+
示例:
|
|
130
|
+
rpc = RPCManager(client)
|
|
131
|
+
rpc.setup("my/rpc/responses")
|
|
132
|
+
"""
|
|
133
|
+
async def handle_bytes(topic: str, payload: bytes):
|
|
134
|
+
"""bytes → RPC message → handle"""
|
|
135
|
+
try:
|
|
136
|
+
# 解码
|
|
137
|
+
message = parse_message_from_bytes(payload, self._codec)
|
|
138
|
+
|
|
139
|
+
# 路由
|
|
140
|
+
if isinstance(message, RPCRequest):
|
|
141
|
+
await self._handle_request(topic, message)
|
|
142
|
+
elif isinstance(message, RPCResponse):
|
|
143
|
+
await self._handle_response(topic, message)
|
|
144
|
+
except MessageError as e:
|
|
145
|
+
logger.debug(f"非 RPC 消息 - topic: {topic}, reason: {e}")
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.exception(f"RPC 消息处理失败: {e}")
|
|
148
|
+
|
|
149
|
+
# 订阅 raw bytes
|
|
150
|
+
self._client.raw.subscribe(reply_topic, handle_bytes)
|
|
151
|
+
|
|
152
|
+
|
|
115
153
|
def register(self, method_name: str):
|
|
116
154
|
"""装饰器:注册本地 RPC 方法供远程调用
|
|
117
155
|
|
|
@@ -257,8 +295,9 @@ class RPCManager:
|
|
|
257
295
|
future = asyncio.get_event_loop().create_future()
|
|
258
296
|
self._pending_calls[request_id] = future
|
|
259
297
|
|
|
260
|
-
#
|
|
261
|
-
|
|
298
|
+
# 发送请求(使用编解码器)
|
|
299
|
+
payload = request.encode(self._codec)
|
|
300
|
+
await self._client.raw.publish(topic, payload, qos=1)
|
|
262
301
|
logger.debug(f"RPC 请求已发送 - method: {method}, request_id: {request_id[:8]}")
|
|
263
302
|
|
|
264
303
|
try:
|
|
@@ -406,5 +445,6 @@ class RPCManager:
|
|
|
406
445
|
topic: 响应主题
|
|
407
446
|
response: RPC 响应消息
|
|
408
447
|
"""
|
|
409
|
-
|
|
448
|
+
payload = response.encode(self._codec)
|
|
449
|
+
await self._client.raw.publish(topic, payload, qos=1)
|
|
410
450
|
logger.debug(f"RPC 响应已发送 - request_id: {response.request_id[:8]}")
|