device-protocol-sdk 1.2.2__tar.gz → 1.2.3__tar.gz

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.
Files changed (18) hide show
  1. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/PKG-INFO +32 -31
  2. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/README.md +17 -17
  3. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/abstract_device.py +216 -216
  4. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/model/action_item.py +11 -11
  5. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/model/device_key.py +7 -7
  6. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/model/device_status.py +35 -35
  7. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/pusher.py +635 -584
  8. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk.egg-info/PKG-INFO +32 -31
  9. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk.egg-info/requires.txt +1 -0
  10. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/setup.cfg +4 -4
  11. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/setup.py +24 -23
  12. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/__init__.py +0 -0
  13. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/device_pb2.py +0 -0
  14. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/device_pb2_grpc.py +0 -0
  15. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk/model/__init__.py +0 -0
  16. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk.egg-info/SOURCES.txt +0 -0
  17. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk.egg-info/dependency_links.txt +0 -0
  18. {device_protocol_sdk-1.2.2 → device_protocol_sdk-1.2.3}/device_protocol_sdk.egg-info/top_level.txt +0 -0
@@ -1,31 +1,32 @@
1
- Metadata-Version: 2.1
2
- Name: device_protocol_sdk
3
- Version: 1.2.2
4
- Summary: 无人设备协议开发SDK
5
- Author: fuhl
6
- Requires-Python: >=3.8
7
- Description-Content-Type: text/markdown
8
- Requires-Dist: grpcio>=1.48.2
9
- Requires-Dist: grpcio-tools>=1.48.2
10
- Requires-Dist: paho-mqtt>=1.6.1
11
- Requires-Dist: pydantic>=1.9.0
12
- Requires-Dist: websockets>=10.0
13
-
14
- # Device Protocol SDK
15
-
16
- 无人机设备协议开发工具包,提供标准化接口实现多类型无人机设备的快速接入。
17
-
18
- [![PyPI Version](https://img.shields.io/pypi/v/device-protocol-sdk)](https://pypi.org/project/device-protocol-sdk/)
19
- [![Python Versions](https://img.shields.io/pypi/pyversions/device-protocol-sdk)](https://pypi.org/project/device-protocol-sdk/)
20
-
21
- ## 功能特性
22
-
23
- - 🛠️ **协议无关抽象**:统一各类无人机设备的控制接口
24
- - 🔌 **连接池管理**:自动维护设备连接,支持多设备并行控制
25
- - 🚀 **实时状态推送**:内置WebSocket状态推送工具
26
- - 🔍 **动态协议发现**:运行时自动加载用户协议实现
27
-
28
- ## 安装
29
-
30
- ```bash
31
- pip install device-protocol-sdk
1
+ Metadata-Version: 2.1
2
+ Name: device_protocol_sdk
3
+ Version: 1.2.3
4
+ Summary: 无人设备协议开发SDK
5
+ Author: fuhl
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: grpcio>=1.48.2
9
+ Requires-Dist: grpcio-tools>=1.48.2
10
+ Requires-Dist: paho-mqtt>=1.6.1
11
+ Requires-Dist: pydantic>=1.9.0
12
+ Requires-Dist: websockets>=10.0
13
+ Requires-Dist: coord-convert>=0.2.1
14
+
15
+ # Device Protocol SDK
16
+
17
+ 无人机设备协议开发工具包,提供标准化接口实现多类型无人机设备的快速接入。
18
+
19
+ [![PyPI Version](https://img.shields.io/pypi/v/device-protocol-sdk)](https://pypi.org/project/device-protocol-sdk/)
20
+ [![Python Versions](https://img.shields.io/pypi/pyversions/device-protocol-sdk)](https://pypi.org/project/device-protocol-sdk/)
21
+
22
+ ## 功能特性
23
+
24
+ - 🛠️ **协议无关抽象**:统一各类无人机设备的控制接口
25
+ - 🔌 **连接池管理**:自动维护设备连接,支持多设备并行控制
26
+ - 🚀 **实时状态推送**:内置WebSocket状态推送工具
27
+ - 🔍 **动态协议发现**:运行时自动加载用户协议实现
28
+
29
+ ## 安装
30
+
31
+ ```bash
32
+ pip install device-protocol-sdk
@@ -1,18 +1,18 @@
1
- # Device Protocol SDK
2
-
3
- 无人机设备协议开发工具包,提供标准化接口实现多类型无人机设备的快速接入。
4
-
5
- [![PyPI Version](https://img.shields.io/pypi/v/device-protocol-sdk)](https://pypi.org/project/device-protocol-sdk/)
6
- [![Python Versions](https://img.shields.io/pypi/pyversions/device-protocol-sdk)](https://pypi.org/project/device-protocol-sdk/)
7
-
8
- ## 功能特性
9
-
10
- - 🛠️ **协议无关抽象**:统一各类无人机设备的控制接口
11
- - 🔌 **连接池管理**:自动维护设备连接,支持多设备并行控制
12
- - 🚀 **实时状态推送**:内置WebSocket状态推送工具
13
- - 🔍 **动态协议发现**:运行时自动加载用户协议实现
14
-
15
- ## 安装
16
-
17
- ```bash
1
+ # Device Protocol SDK
2
+
3
+ 无人机设备协议开发工具包,提供标准化接口实现多类型无人机设备的快速接入。
4
+
5
+ [![PyPI Version](https://img.shields.io/pypi/v/device-protocol-sdk)](https://pypi.org/project/device-protocol-sdk/)
6
+ [![Python Versions](https://img.shields.io/pypi/pyversions/device-protocol-sdk)](https://pypi.org/project/device-protocol-sdk/)
7
+
8
+ ## 功能特性
9
+
10
+ - 🛠️ **协议无关抽象**:统一各类无人机设备的控制接口
11
+ - 🔌 **连接池管理**:自动维护设备连接,支持多设备并行控制
12
+ - 🚀 **实时状态推送**:内置WebSocket状态推送工具
13
+ - 🔍 **动态协议发现**:运行时自动加载用户协议实现
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
18
  pip install device-protocol-sdk
@@ -1,217 +1,217 @@
1
- from abc import ABC, abstractmethod
2
- import threading
3
- import json,uuid,time
4
- from typing import List
5
- from collections import defaultdict
6
- from .model.device_key import DeviceKey
7
- from .model.action_item import ActionItem
8
- from .model.device_status import DeviceStatus,MessageType,MessageLevel
9
- from typing import Dict, Any, Optional
10
- import grpc
11
- from . import device_pb2
12
-
13
- from contextlib import contextmanager
14
- from typing import Generator
15
- import logging
16
- logger = logging.getLogger(__name__)
17
-
18
-
19
-
20
-
21
-
22
- class AbstractDevice(ABC):
23
- _status_cache: Dict[DeviceKey, dict] = {}
24
- _connection_pool: Dict[DeviceKey, Any] = {} # 类级连接池
25
- _lock = threading.RLock()
26
-
27
- _monitor_flags = defaultdict(bool) # 状态监控标志
28
- _monitor_futures = {}
29
- _lock = threading.Lock() # 线程安全锁
30
- _status_lock = threading.RLock() # 新增:状态读取可重入锁
31
-
32
- def __init__(self):
33
- self._grpc_stub = None
34
- self._current_mission_id = None
35
-
36
- def set_grpc_stub(self, grpc_stub):
37
- """设置gRPC stub用于发送消息"""
38
- self._grpc_stub = grpc_stub
39
-
40
- def set_current_mission_id(self, mission_id: str):
41
- """设置当前任务的mission_id"""
42
- self._current_mission_id = mission_id
43
-
44
- @contextmanager
45
- def mission_context(self, mission_id: str) -> Generator[None, None, None]:
46
- """为任务提供独立的上下文"""
47
- original_mission_id = getattr(self, '_current_mission_id', None)
48
- try:
49
- self._current_mission_id = mission_id
50
- yield
51
- finally:
52
- self._current_mission_id = original_mission_id
53
- def send_message(self, device_id: int, message_type: MessageType, content: str,level:MessageLevel,
54
- metadata: Optional[Dict[str, Any]] = None,
55
- mission_id: Optional[str] = None) -> bool:
56
- """
57
- 通过gRPC向服务端发送消息
58
-
59
- Args:
60
- message_type: 消息类型
61
- content: 消息内容
62
- metadata: 附加元数据
63
- mission_id: 任务ID,如果为None则使用当前任务ID
64
-
65
- Returns:
66
- bool: 发送是否成功
67
- """
68
- target_mission_id = mission_id or self._current_mission_id
69
- if not target_mission_id:
70
- logger.warning("未提供mission_id且当前无任务上下文")
71
- return False
72
-
73
- if not self._grpc_stub:
74
- logger.warning("gRPC stub未设置,无法发送消息")
75
- return False
76
-
77
- # 确定mission_id
78
- target_mission_id = mission_id or self._current_mission_id
79
- if not target_mission_id:
80
- logger.warning("未提供mission_id且当前无任务ID,无法发送消息")
81
- return False
82
-
83
- try:
84
- # 构建gRPC消息
85
- device_message = device_pb2.DeviceMessage(
86
- mission_id=target_mission_id,
87
- message_type=message_type.value,
88
- message_level=level.value,
89
- content=content,
90
- timestamp=int(time.time()),
91
- metadata=json.dumps(metadata) if metadata else "",
92
- device_id=int(device_id)
93
- )
94
-
95
- # 发送消息(假设服务端有ReceiveDeviceMessage方法)
96
- self._grpc_stub.ReceiveDeviceMessage(device_message)
97
-
98
- except grpc.RpcError as e:
99
- logger.error(f"gRPC消息发送失败: {e}")
100
- return False
101
- except Exception as e:
102
- logger.error(f"发送消息时发生异常: {e}")
103
- return False
104
-
105
- def send_text_message(self, device_id: int, text: str,level:MessageLevel, **kwargs) -> bool:
106
- """发送纯文本消息的快捷方法"""
107
- return self.send_message(device_id,MessageType.TEXT, text,level, **kwargs)
108
-
109
- def send_image_message(self, device_id: int, image_data_or_url: str,level:MessageLevel, **kwargs) -> bool:
110
- """发送图像消息的快捷方法"""
111
- metadata = kwargs.pop('metadata', {})
112
- if 'description' not in metadata:
113
- metadata['description'] = '设备上传的图像'
114
- return self.send_message(device_id,MessageType.IMAGE, image_data_or_url,level, metadata, **kwargs)
115
-
116
- def send_video_url_message(self, device_id: int, video_url: str,level:MessageLevel, **kwargs) -> bool:
117
- """发送视频URL消息的快捷方法"""
118
- metadata = kwargs.pop('metadata', {})
119
- if 'description' not in metadata:
120
- metadata['description'] = '设备视频流地址'
121
- return self.send_message(device_id,MessageType.VIDEO_URL, video_url,level, metadata, **kwargs)
122
-
123
- @property
124
- @abstractmethod
125
- def protocol_name(self) -> str:
126
- pass
127
-
128
- def is_cluster(self):
129
- return 0
130
-
131
- @abstractmethod
132
- def get_action_list(self) -> List[ActionItem]:
133
- pass
134
- def connect(self, device_key: DeviceKey) -> Any:
135
- """带设备ID的连接池实现"""
136
- with self._lock:
137
- if device_key not in self._connection_pool:
138
- is_connect,client = self._create_client(device_key.device_id,device_key.connection_str)
139
- if is_connect:
140
- self._connection_pool[device_key] = client
141
-
142
- def disconnect(self, device_key: DeviceKey):
143
- with self._lock:
144
- if device_key in self._connection_pool:
145
- self._close_client(self._connection_pool[device_key],device_key.device_id,device_key.connection_str)
146
- # 清空连接池中对应device_key的数据
147
- del self._connection_pool[device_key]
148
- logger.info(f"已从连接池中移除设备 {device_key}")
149
-
150
-
151
- def is_connected(self, device_key: DeviceKey) -> bool:
152
- with self._lock:
153
- if conn := self._connection_pool.get(device_key):
154
- return True
155
- return False
156
-
157
- @abstractmethod
158
- def _create_client(self,device_id:int, connection_str: str) -> tuple[bool, Any]:
159
- pass
160
-
161
- @abstractmethod
162
- def _close_client(self,client, device_id: int, connection_str: str) -> Any:
163
- pass
164
-
165
- def get_device_status(self,client,device_id:str,connection_str:str) -> DeviceStatus:
166
- pass
167
-
168
- def get_device_status_list(self,client,device_id:str,connection_str:str) -> List[DeviceStatus]:
169
- pass
170
-
171
- def _ensure_connection(self, device_key: DeviceKey) -> Any:
172
- """确保设备连接存在,如果不存在则创建"""
173
- client = self._connection_pool.get(device_key)
174
- if client is not None:
175
- return client
176
-
177
- # 尝试连接
178
- success, result = self.connect(device_key)
179
- if not success:
180
- raise ConnectionError(f"无法连接设备 {device_key.device_id}: {result}")
181
-
182
- client = self._connection_pool.get(device_key)
183
- if client is None:
184
- raise ConnectionError(f"连接已建立但客户端对象缺失: {device_key.device_id}")
185
-
186
- return client
187
-
188
- def get_status(self, device_id: str, connection_str: str) -> DeviceStatus:
189
- my_device = DeviceKey(device_id=device_id, connection_str=connection_str)
190
-
191
- # 确保连接存在
192
- client = self._ensure_connection(my_device)
193
- is_cluster = self.is_cluster()
194
- if is_cluster == 0:
195
- return [self.get_device_status(client, device_id, connection_str)]
196
- else:
197
- return self.get_device_status_list(client, device_id, connection_str)
198
-
199
- def excute_command(self,device_id:str,connection_str:str, command: str, params: Dict[str, Any],
200
- mission_id: str):
201
- my_device = DeviceKey(device_id=device_id, connection_str=connection_str)
202
- this_client = self._ensure_connection(my_device)
203
- with self.mission_context(mission_id):
204
- self.execute(this_client,device_id,connection_str,command,params)
205
-
206
- @abstractmethod
207
- def execute(self,client,device_id:int,connection_str:str, command: str, params: Dict[str, Any]) -> Dict[str, Any]:
208
- """执行控制指令"""
209
-
210
- def to_json(self):
211
- """序列化设备信息"""
212
- actions = [item.dict() for item in self.get_action_list()]
213
- return json.dumps({
214
- "protocol": self.protocol_name,
215
- "action_list": actions,
216
- "is_cluster": self.is_cluster()
1
+ from abc import ABC, abstractmethod
2
+ import threading
3
+ import json,uuid,time
4
+ from typing import List
5
+ from collections import defaultdict
6
+ from .model.device_key import DeviceKey
7
+ from .model.action_item import ActionItem
8
+ from .model.device_status import DeviceStatus,MessageType,MessageLevel
9
+ from typing import Dict, Any, Optional
10
+ import grpc
11
+ from . import device_pb2
12
+
13
+ from contextlib import contextmanager
14
+ from typing import Generator
15
+ import logging
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+
20
+
21
+
22
+ class AbstractDevice(ABC):
23
+ _status_cache: Dict[DeviceKey, dict] = {}
24
+ _connection_pool: Dict[DeviceKey, Any] = {} # 类级连接池
25
+ _lock = threading.RLock()
26
+
27
+ _monitor_flags = defaultdict(bool) # 状态监控标志
28
+ _monitor_futures = {}
29
+ _lock = threading.Lock() # 线程安全锁
30
+ _status_lock = threading.RLock() # 新增:状态读取可重入锁
31
+
32
+ def __init__(self):
33
+ self._grpc_stub = None
34
+ self._current_mission_id = None
35
+
36
+ def set_grpc_stub(self, grpc_stub):
37
+ """设置gRPC stub用于发送消息"""
38
+ self._grpc_stub = grpc_stub
39
+
40
+ def set_current_mission_id(self, mission_id: str):
41
+ """设置当前任务的mission_id"""
42
+ self._current_mission_id = mission_id
43
+
44
+ @contextmanager
45
+ def mission_context(self, mission_id: str) -> Generator[None, None, None]:
46
+ """为任务提供独立的上下文"""
47
+ original_mission_id = getattr(self, '_current_mission_id', None)
48
+ try:
49
+ self._current_mission_id = mission_id
50
+ yield
51
+ finally:
52
+ self._current_mission_id = original_mission_id
53
+ def send_message(self, device_id: int, message_type: MessageType, content: str,level:MessageLevel,
54
+ metadata: Optional[Dict[str, Any]] = None,
55
+ mission_id: Optional[str] = None) -> bool:
56
+ """
57
+ 通过gRPC向服务端发送消息
58
+
59
+ Args:
60
+ message_type: 消息类型
61
+ content: 消息内容
62
+ metadata: 附加元数据
63
+ mission_id: 任务ID,如果为None则使用当前任务ID
64
+
65
+ Returns:
66
+ bool: 发送是否成功
67
+ """
68
+ target_mission_id = mission_id or self._current_mission_id
69
+ if not target_mission_id:
70
+ logger.warning("未提供mission_id且当前无任务上下文")
71
+ return False
72
+
73
+ if not self._grpc_stub:
74
+ logger.warning("gRPC stub未设置,无法发送消息")
75
+ return False
76
+
77
+ # 确定mission_id
78
+ target_mission_id = mission_id or self._current_mission_id
79
+ if not target_mission_id:
80
+ logger.warning("未提供mission_id且当前无任务ID,无法发送消息")
81
+ return False
82
+
83
+ try:
84
+ # 构建gRPC消息
85
+ device_message = device_pb2.DeviceMessage(
86
+ mission_id=target_mission_id,
87
+ message_type=message_type.value,
88
+ message_level=level.value,
89
+ content=content,
90
+ timestamp=int(time.time()),
91
+ metadata=json.dumps(metadata) if metadata else "",
92
+ device_id=int(device_id)
93
+ )
94
+
95
+ # 发送消息(假设服务端有ReceiveDeviceMessage方法)
96
+ self._grpc_stub.ReceiveDeviceMessage(device_message)
97
+
98
+ except grpc.RpcError as e:
99
+ logger.error(f"gRPC消息发送失败: {e}")
100
+ return False
101
+ except Exception as e:
102
+ logger.error(f"发送消息时发生异常: {e}")
103
+ return False
104
+
105
+ def send_text_message(self, device_id: int, text: str,level:MessageLevel, **kwargs) -> bool:
106
+ """发送纯文本消息的快捷方法"""
107
+ return self.send_message(device_id,MessageType.TEXT, text,level, **kwargs)
108
+
109
+ def send_image_message(self, device_id: int, image_data_or_url: str,level:MessageLevel, **kwargs) -> bool:
110
+ """发送图像消息的快捷方法"""
111
+ metadata = kwargs.pop('metadata', {})
112
+ if 'description' not in metadata:
113
+ metadata['description'] = '设备上传的图像'
114
+ return self.send_message(device_id,MessageType.IMAGE, image_data_or_url,level, metadata, **kwargs)
115
+
116
+ def send_video_url_message(self, device_id: int, video_url: str,level:MessageLevel, **kwargs) -> bool:
117
+ """发送视频URL消息的快捷方法"""
118
+ metadata = kwargs.pop('metadata', {})
119
+ if 'description' not in metadata:
120
+ metadata['description'] = '设备视频流地址'
121
+ return self.send_message(device_id,MessageType.VIDEO_URL, video_url,level, metadata, **kwargs)
122
+
123
+ @property
124
+ @abstractmethod
125
+ def protocol_name(self) -> str:
126
+ pass
127
+
128
+ def is_cluster(self):
129
+ return 0
130
+
131
+ @abstractmethod
132
+ def get_action_list(self) -> List[ActionItem]:
133
+ pass
134
+ def connect(self, device_key: DeviceKey) -> Any:
135
+ """带设备ID的连接池实现"""
136
+ with self._lock:
137
+ if device_key not in self._connection_pool:
138
+ is_connect,client = self._create_client(device_key.device_id,device_key.connection_str)
139
+ if is_connect:
140
+ self._connection_pool[device_key] = client
141
+
142
+ def disconnect(self, device_key: DeviceKey):
143
+ with self._lock:
144
+ if device_key in self._connection_pool:
145
+ self._close_client(self._connection_pool[device_key],device_key.device_id,device_key.connection_str)
146
+ # 清空连接池中对应device_key的数据
147
+ del self._connection_pool[device_key]
148
+ logger.info(f"已从连接池中移除设备 {device_key}")
149
+
150
+
151
+ def is_connected(self, device_key: DeviceKey) -> bool:
152
+ with self._lock:
153
+ if conn := self._connection_pool.get(device_key):
154
+ return True
155
+ return False
156
+
157
+ @abstractmethod
158
+ def _create_client(self,device_id:int, connection_str: str) -> tuple[bool, Any]:
159
+ pass
160
+
161
+ @abstractmethod
162
+ def _close_client(self,client, device_id: int, connection_str: str) -> Any:
163
+ pass
164
+
165
+ def get_device_status(self,client,device_id:str,connection_str:str) -> DeviceStatus:
166
+ pass
167
+
168
+ def get_device_status_list(self,client,device_id:str,connection_str:str) -> List[DeviceStatus]:
169
+ pass
170
+
171
+ def _ensure_connection(self, device_key: DeviceKey) -> Any:
172
+ """确保设备连接存在,如果不存在则创建"""
173
+ client = self._connection_pool.get(device_key)
174
+ if client is not None:
175
+ return client
176
+
177
+ # 尝试连接
178
+ success, result = self.connect(device_key)
179
+ if not success:
180
+ raise ConnectionError(f"无法连接设备 {device_key.device_id}: {result}")
181
+
182
+ client = self._connection_pool.get(device_key)
183
+ if client is None:
184
+ raise ConnectionError(f"连接已建立但客户端对象缺失: {device_key.device_id}")
185
+
186
+ return client
187
+
188
+ def get_status(self, device_id: str, connection_str: str) -> DeviceStatus:
189
+ my_device = DeviceKey(device_id=device_id, connection_str=connection_str)
190
+
191
+ # 确保连接存在
192
+ client = self._ensure_connection(my_device)
193
+ is_cluster = self.is_cluster()
194
+ if is_cluster == 0:
195
+ return [self.get_device_status(client, device_id, connection_str)]
196
+ else:
197
+ return self.get_device_status_list(client, device_id, connection_str)
198
+
199
+ def excute_command(self,device_id:str,connection_str:str, command: str, params: Dict[str, Any],
200
+ mission_id: str):
201
+ my_device = DeviceKey(device_id=device_id, connection_str=connection_str)
202
+ this_client = self._ensure_connection(my_device)
203
+ with self.mission_context(mission_id):
204
+ self.execute(this_client,device_id,connection_str,command,params)
205
+
206
+ @abstractmethod
207
+ def execute(self,client,device_id:int,connection_str:str, command: str, params: Dict[str, Any]) -> Dict[str, Any]:
208
+ """执行控制指令"""
209
+
210
+ def to_json(self):
211
+ """序列化设备信息"""
212
+ actions = [item.dict() for item in self.get_action_list()]
213
+ return json.dumps({
214
+ "protocol": self.protocol_name,
215
+ "action_list": actions,
216
+ "is_cluster": self.is_cluster()
217
217
  }, ensure_ascii=False)
@@ -1,12 +1,12 @@
1
- # action_item.py
2
- from dataclasses import dataclass, asdict
3
- from typing import Dict, Any
4
- @dataclass
5
- class ActionItem:
6
- name: str # 动作名称
7
- command_type: str # 动作唯一标识
8
- description: str # 描述
9
- params: Dict[str, Any] # JSON Schema 或简单 dict 描述
10
-
11
- def dict(self) -> Dict[str, Any]:
1
+ # action_item.py
2
+ from dataclasses import dataclass, asdict
3
+ from typing import Dict, Any
4
+ @dataclass
5
+ class ActionItem:
6
+ name: str # 动作名称
7
+ command_type: str # 动作唯一标识
8
+ description: str # 描述
9
+ params: Dict[str, Any] # JSON Schema 或简单 dict 描述
10
+
11
+ def dict(self) -> Dict[str, Any]:
12
12
  return asdict(self)
@@ -1,8 +1,8 @@
1
- # device_key.py
2
- from dataclasses import dataclass
3
-
4
- @dataclass(frozen=True)
5
- class DeviceKey:
6
- """不可变的设备唯一标识"""
7
- device_id: str # 业务唯一字符串
1
+ # device_key.py
2
+ from dataclasses import dataclass
3
+
4
+ @dataclass(frozen=True)
5
+ class DeviceKey:
6
+ """不可变的设备唯一标识"""
7
+ device_id: str # 业务唯一字符串
8
8
  connection_str: str # 连接参数(如IP/串口号)
@@ -1,35 +1,35 @@
1
- # device_status.py
2
- from typing import TypedDict, Optional
3
- from enum import Enum
4
-
5
- class DeviceStatus(TypedDict, total=False):
6
- """设备状态固定格式"""
7
- is_lock: Optional[int] # 必填
8
- heartbeat: Optional[int] # 必填
9
- battery: Optional[float] # 必填
10
- airspeed: Optional[float] # 必填
11
- groundspeed: Optional[float] # 必填
12
- yaw_degrees: Optional[float] # 必填
13
- roll: Optional[float] # 必填
14
- pitch: Optional[float] # 必填
15
- yaw: Optional[float] # 必填
16
- lat: Optional[float] # 必填
17
- lon: Optional[float] # 必填
18
- alt: Optional[float] # 必填
19
- vzspeed: Optional[float] # 必填
20
- height: Optional[float] # 必填
21
-
22
-
23
- class MessageType(Enum):
24
- TEXT = "text" # 纯文本消息
25
- IMAGE = "image" # 图像消息
26
- VIDEO_URL = "video_url" # 视频流URL
27
- PROGRESS = "progress" # 进度更新
28
- STATUS = "status" # 状态更新
29
-
30
- #消息级别: failed, warning, info, success
31
- class MessageLevel(Enum):
32
- FAILED = "failed"
33
- WARNING = "warning"
34
- INFO = "info"
35
- SUCCESS = "success"
1
+ # device_status.py
2
+ from typing import TypedDict, Optional
3
+ from enum import Enum
4
+
5
+ class DeviceStatus(TypedDict, total=False):
6
+ """设备状态固定格式"""
7
+ is_lock: Optional[int] # 必填
8
+ heartbeat: Optional[int] # 必填
9
+ battery: Optional[float] # 必填
10
+ airspeed: Optional[float] # 必填
11
+ groundspeed: Optional[float] # 必填
12
+ yaw_degrees: Optional[float] # 必填
13
+ roll: Optional[float] # 必填
14
+ pitch: Optional[float] # 必填
15
+ yaw: Optional[float] # 必填
16
+ lat: Optional[float] # 必填
17
+ lon: Optional[float] # 必填
18
+ alt: Optional[float] # 必填
19
+ vzspeed: Optional[float] # 必填
20
+ height: Optional[float] # 必填
21
+
22
+
23
+ class MessageType(Enum):
24
+ TEXT = "text" # 纯文本消息
25
+ IMAGE = "image" # 图像消息
26
+ VIDEO_URL = "video_url" # 视频流URL
27
+ PROGRESS = "progress" # 进度更新
28
+ STATUS = "status" # 状态更新
29
+
30
+ #消息级别: failed, warning, info, success
31
+ class MessageLevel(Enum):
32
+ FAILED = "failed"
33
+ WARNING = "warning"
34
+ INFO = "info"
35
+ SUCCESS = "success"