hex-zmq-servers 0.3.9__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.
Files changed (110) hide show
  1. hex_zmq_servers/__init__.py +173 -0
  2. hex_zmq_servers/cam/__init__.py +52 -0
  3. hex_zmq_servers/cam/berxel/__init__.py +17 -0
  4. hex_zmq_servers/cam/berxel/cam_berxel.py +282 -0
  5. hex_zmq_servers/cam/berxel/cam_berxel_cli.py +33 -0
  6. hex_zmq_servers/cam/berxel/cam_berxel_srv.py +79 -0
  7. hex_zmq_servers/cam/cam_base.py +189 -0
  8. hex_zmq_servers/cam/dummy/__init__.py +17 -0
  9. hex_zmq_servers/cam/dummy/cam_dummy.py +69 -0
  10. hex_zmq_servers/cam/dummy/cam_dummy_cli.py +29 -0
  11. hex_zmq_servers/cam/dummy/cam_dummy_srv.py +68 -0
  12. hex_zmq_servers/cam/realsense/__init__.py +17 -0
  13. hex_zmq_servers/cam/realsense/cam_realsense.py +159 -0
  14. hex_zmq_servers/cam/realsense/cam_realsense_cli.py +33 -0
  15. hex_zmq_servers/cam/realsense/cam_realsense_srv.py +78 -0
  16. hex_zmq_servers/cam/rgb/__init__.py +17 -0
  17. hex_zmq_servers/cam/rgb/cam_rgb.py +135 -0
  18. hex_zmq_servers/cam/rgb/cam_rgb_cli.py +43 -0
  19. hex_zmq_servers/cam/rgb/cam_rgb_srv.py +78 -0
  20. hex_zmq_servers/config/cam_berxel.json +18 -0
  21. hex_zmq_servers/config/cam_dummy.json +12 -0
  22. hex_zmq_servers/config/cam_realsense.json +17 -0
  23. hex_zmq_servers/config/cam_rgb.json +28 -0
  24. hex_zmq_servers/config/mujoco_archer_y6.json +37 -0
  25. hex_zmq_servers/config/mujoco_e3_desktop.json +41 -0
  26. hex_zmq_servers/config/robot_dummy.json +153 -0
  27. hex_zmq_servers/config/robot_gello.json +66 -0
  28. hex_zmq_servers/config/robot_hexarm.json +37 -0
  29. hex_zmq_servers/config/zmq_dummy.json +12 -0
  30. hex_zmq_servers/device_base.py +44 -0
  31. hex_zmq_servers/hex_launch.py +489 -0
  32. hex_zmq_servers/mujoco/__init__.py +28 -0
  33. hex_zmq_servers/mujoco/archer_y6/__init__.py +17 -0
  34. hex_zmq_servers/mujoco/archer_y6/model/assets/arm_base_link.STL +0 -0
  35. hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_1.STL +0 -0
  36. hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_2.STL +0 -0
  37. hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_3.STL +0 -0
  38. hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_4.STL +0 -0
  39. hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_5.STL +0 -0
  40. hex_zmq_servers/mujoco/archer_y6/model/assets/assets.xml +17 -0
  41. hex_zmq_servers/mujoco/archer_y6/model/assets/camera_link.STL +0 -0
  42. hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_base_link.STL +0 -0
  43. hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_left_helper_link.STL +0 -0
  44. hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_left_link_1.STL +0 -0
  45. hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_left_link_2.STL +0 -0
  46. hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_right_helper_link.STL +0 -0
  47. hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_right_link_1.STL +0 -0
  48. hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_right_link_2.STL +0 -0
  49. hex_zmq_servers/mujoco/archer_y6/model/assets/table_link.STL +0 -0
  50. hex_zmq_servers/mujoco/archer_y6/model/robot.xml +95 -0
  51. hex_zmq_servers/mujoco/archer_y6/model/scene.xml +51 -0
  52. hex_zmq_servers/mujoco/archer_y6/model/setting.xml +37 -0
  53. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6.py +325 -0
  54. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_cli.py +71 -0
  55. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_srv.py +148 -0
  56. hex_zmq_servers/mujoco/e3_desktop/__init__.py +17 -0
  57. hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_base_link.STL +0 -0
  58. hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_1.STL +0 -0
  59. hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_2.STL +0 -0
  60. hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_3.STL +0 -0
  61. hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_4.STL +0 -0
  62. hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_5.STL +0 -0
  63. hex_zmq_servers/mujoco/e3_desktop/model/assets/assets.xml +18 -0
  64. hex_zmq_servers/mujoco/e3_desktop/model/assets/camera_link.STL +0 -0
  65. hex_zmq_servers/mujoco/e3_desktop/model/assets/e3_desktop_base_link.STL +0 -0
  66. hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_base_link.STL +0 -0
  67. hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_left_helper_link.STL +0 -0
  68. hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_left_link_1.STL +0 -0
  69. hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_left_link_2.STL +0 -0
  70. hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_right_helper_link.STL +0 -0
  71. hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_right_link_1.STL +0 -0
  72. hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_right_link_2.STL +0 -0
  73. hex_zmq_servers/mujoco/e3_desktop/model/assets/table_link.STL +0 -0
  74. hex_zmq_servers/mujoco/e3_desktop/model/robot.xml +188 -0
  75. hex_zmq_servers/mujoco/e3_desktop/model/scene.xml +53 -0
  76. hex_zmq_servers/mujoco/e3_desktop/model/setting.xml +72 -0
  77. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop.py +449 -0
  78. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_cli.py +289 -0
  79. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_srv.py +244 -0
  80. hex_zmq_servers/mujoco/mujoco_base.py +425 -0
  81. hex_zmq_servers/robot/__init__.py +37 -0
  82. hex_zmq_servers/robot/dummy/__init__.py +17 -0
  83. hex_zmq_servers/robot/dummy/robot_dummy.py +94 -0
  84. hex_zmq_servers/robot/dummy/robot_dummy_cli.py +29 -0
  85. hex_zmq_servers/robot/dummy/robot_dummy_srv.py +82 -0
  86. hex_zmq_servers/robot/gello/__init__.py +17 -0
  87. hex_zmq_servers/robot/gello/robot_gello.py +366 -0
  88. hex_zmq_servers/robot/gello/robot_gello_cli.py +29 -0
  89. hex_zmq_servers/robot/gello/robot_gello_srv.py +93 -0
  90. hex_zmq_servers/robot/hexarm/__init__.py +47 -0
  91. hex_zmq_servers/robot/hexarm/robot_hexarm.py +292 -0
  92. hex_zmq_servers/robot/hexarm/robot_hexarm_cli.py +37 -0
  93. hex_zmq_servers/robot/hexarm/robot_hexarm_srv.py +87 -0
  94. hex_zmq_servers/robot/hexarm/urdf/archer_l6y/empty.urdf +206 -0
  95. hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100.urdf +206 -0
  96. hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100_handle.urdf +206 -0
  97. hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100_p050.urdf +206 -0
  98. hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100_p050_handle.urdf +206 -0
  99. hex_zmq_servers/robot/hexarm/urdf/archer_y6/empty.urdf +207 -0
  100. hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100.urdf +207 -0
  101. hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_handle.urdf +207 -0
  102. hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_p050.urdf +207 -0
  103. hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_p050_handle.urdf +207 -0
  104. hex_zmq_servers/robot/robot_base.py +276 -0
  105. hex_zmq_servers/zmq_base.py +547 -0
  106. hex_zmq_servers-0.3.9.dist-info/METADATA +147 -0
  107. hex_zmq_servers-0.3.9.dist-info/RECORD +110 -0
  108. hex_zmq_servers-0.3.9.dist-info/WHEEL +5 -0
  109. hex_zmq_servers-0.3.9.dist-info/licenses/LICENSE +201 -0
  110. hex_zmq_servers-0.3.9.dist-info/top_level.txt +1 -0
@@ -0,0 +1,425 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ ################################################################
4
+ # Copyright 2025 Dong Zhaorui. All rights reserved.
5
+ # Author: Dong Zhaorui 847235539@qq.com
6
+ # Date : 2025-09-16
7
+ ################################################################
8
+
9
+ import threading
10
+ import numpy as np
11
+ from collections import deque
12
+ from abc import abstractmethod
13
+
14
+ from ..device_base import HexDeviceBase
15
+ from ..zmq_base import (
16
+ hex_zmq_ts_now,
17
+ HexZMQClientBase,
18
+ HexZMQServerBase,
19
+ )
20
+
21
+ NET_CONFIG = {
22
+ "ip": "127.0.0.1",
23
+ "port": 12345,
24
+ "realtime_mode": False,
25
+ "deque_maxlen": 10,
26
+ "client_timeout_ms": 200,
27
+ "server_timeout_ms": 1_000,
28
+ "server_num_workers": 4,
29
+ }
30
+
31
+ TAU = 2 * np.pi
32
+
33
+
34
+ class HexMujocoBase(HexDeviceBase):
35
+
36
+ def __init__(self, realtime_mode: bool = False):
37
+ HexDeviceBase.__init__(self, realtime_mode)
38
+ self._dofs = None
39
+ self._limits = None
40
+ self._intri = None
41
+
42
+ def __del__(self):
43
+ HexDeviceBase.__del__(self)
44
+
45
+ def is_working(self) -> bool:
46
+ return self._working.is_set()
47
+
48
+ def get_dofs(self) -> np.ndarray:
49
+ self._wait_for_working()
50
+ return np.array(self._dofs, dtype=np.uint8)
51
+
52
+ def get_limits(self) -> np.ndarray:
53
+ self._wait_for_working()
54
+ return self._limits
55
+
56
+ def get_intri(self) -> np.ndarray:
57
+ self._wait_for_working()
58
+ return self._intri
59
+
60
+ @staticmethod
61
+ def _rads_normalize(rads: np.ndarray) -> np.ndarray:
62
+ return (rads + np.pi) % TAU - np.pi
63
+
64
+ @staticmethod
65
+ def _apply_pos_limits(
66
+ rads: np.ndarray,
67
+ lower_bound: np.ndarray,
68
+ upper_bound: np.ndarray,
69
+ ) -> np.ndarray:
70
+ normed_rads = HexMujocoBase._rads_normalize(rads)
71
+ outside = (normed_rads < lower_bound) | (normed_rads > upper_bound)
72
+ if not np.any(outside):
73
+ return normed_rads
74
+
75
+ lower_dist = np.fabs(
76
+ HexMujocoBase._rads_normalize(
77
+ (normed_rads - lower_bound)[outside]))
78
+ upper_dist = np.fabs(
79
+ HexMujocoBase._rads_normalize(
80
+ (normed_rads - upper_bound)[outside]))
81
+ choose_lower = lower_dist < upper_dist
82
+ choose_upper = ~choose_lower
83
+
84
+ outside_full = np.flatnonzero(outside)
85
+ outside_lower = outside_full[choose_lower]
86
+ outside_upper = outside_full[choose_upper]
87
+ normed_rads[outside_lower] = lower_bound[outside_lower]
88
+ normed_rads[outside_upper] = upper_bound[outside_upper]
89
+
90
+ return normed_rads
91
+
92
+ @abstractmethod
93
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
94
+ raise NotImplementedError(
95
+ "`work_loop` should be implemented by the child class")
96
+
97
+ @abstractmethod
98
+ def close(self):
99
+ raise NotImplementedError(
100
+ "`close` should be implemented by the child class")
101
+
102
+
103
+ class HexMujocoClientBase(HexZMQClientBase):
104
+
105
+ def __init__(self, net_config: dict = NET_CONFIG):
106
+ HexZMQClientBase.__init__(self, net_config)
107
+ self._states_seq = {
108
+ "robot": 0,
109
+ "obj": 0,
110
+ }
111
+ self._used_states_queue = {
112
+ "robot": 0,
113
+ "obj": 0,
114
+ }
115
+ self._states_queue = {
116
+ "robot": deque(maxlen=self._max_seq_num),
117
+ "obj": deque(maxlen=self._max_seq_num),
118
+ }
119
+ self._camera_seq = {
120
+ "rgb": 0,
121
+ "depth": 0,
122
+ }
123
+ self._used_camera_seq = {
124
+ "rgb": 0,
125
+ "depth": 0,
126
+ }
127
+ self._camera_queue = {
128
+ "rgb": deque(maxlen=self._deque_maxlen),
129
+ "depth": deque(maxlen=self._deque_maxlen),
130
+ }
131
+ self._cmds_seq = 0
132
+ self._cmds_queue = deque(maxlen=self._deque_maxlen)
133
+
134
+ def __del__(self):
135
+ HexZMQClientBase.__del__(self)
136
+
137
+ def reset(self):
138
+ self._states_seq = {
139
+ "robot": 0,
140
+ "obj": 0,
141
+ }
142
+ self._used_states_queue = {
143
+ "robot": 0,
144
+ "obj": 0,
145
+ }
146
+ self._camera_seq = {
147
+ "rgb": 0,
148
+ "depth": 0,
149
+ }
150
+ self._used_camera_seq = {
151
+ "rgb": 0,
152
+ "depth": 0,
153
+ }
154
+ self._cmds_seq = 0
155
+
156
+ hdr, _ = self.request({"cmd": "reset"})
157
+ return hdr
158
+
159
+ def seq_clear(self):
160
+ clear_hdr, _ = self.request({"cmd": "seq_clear"})
161
+ return clear_hdr
162
+
163
+ def get_dofs(self):
164
+ _, dofs = self.request({"cmd": "get_dofs"})
165
+ return dofs
166
+
167
+ def get_limits(self):
168
+ _, limits = self.request({"cmd": "get_limits"})
169
+ return limits
170
+
171
+ def get_states(self, robot_name: str | None = None, newest: bool = False):
172
+ try:
173
+ if self._realtime_mode or newest:
174
+ hdr, states = self._states_queue[robot_name][-1]
175
+ if self._used_states_queue[robot_name] != hdr["args"]:
176
+ self._used_states_queue[robot_name] = hdr["args"]
177
+ return hdr, states
178
+ else:
179
+ return None, None
180
+ else:
181
+ return self._states_queue[robot_name].popleft()
182
+ except IndexError:
183
+ return None, None
184
+ except KeyError:
185
+ print(f"\033[91munknown robot name: {robot_name}\033[0m")
186
+ return None, None
187
+
188
+ def get_rgb(self, camera_name: str | None = None, newest: bool = False):
189
+ try:
190
+ if self._realtime_mode or newest:
191
+ hdr, img = self._camera_queue["rgb"][-1]
192
+ if self._used_camera_seq["rgb"] != hdr["args"]:
193
+ self._used_camera_seq["rgb"] = hdr["args"]
194
+ return hdr, img
195
+ else:
196
+ return None, None
197
+ else:
198
+ return self._camera_queue["rgb"].popleft()
199
+ except IndexError:
200
+ return None, None
201
+
202
+ def get_depth(self, camera_name: str | None = None, newest: bool = False):
203
+ try:
204
+ if self._realtime_mode or newest:
205
+ hdr, img = self._camera_queue["depth"][-1]
206
+ if self._used_camera_seq["depth"] != hdr["args"]:
207
+ self._used_camera_seq["depth"] = hdr["args"]
208
+ return hdr, img
209
+ else:
210
+ return None, None
211
+ else:
212
+ return self._camera_queue["depth"].popleft()
213
+ except IndexError:
214
+ return None, None
215
+
216
+ def set_cmds(self, cmds: np.ndarray):
217
+ self._cmds_queue.append(cmds)
218
+
219
+ def get_intri(self):
220
+ intri_hdr, intri = self.request({"cmd": "get_intri"})
221
+ return intri_hdr, intri
222
+
223
+ def _get_rgb_inner(self, camera_name: str | None = None):
224
+ return self._process_frame(camera_name, False)
225
+
226
+ def _get_depth_inner(self, camera_name: str | None = None):
227
+ return self._process_frame(camera_name, True)
228
+
229
+ def _process_frame(
230
+ self,
231
+ camera_name: str | None = None,
232
+ depth_flag: bool = False,
233
+ ):
234
+ req_cmd = f"get_{'depth' if depth_flag else 'rgb'}"
235
+ if camera_name is not None:
236
+ req_cmd += f"_{camera_name}"
237
+
238
+ hdr, img = self.request({
239
+ "cmd":
240
+ req_cmd,
241
+ "args": (1 + self._camera_seq['depth' if depth_flag else 'rgb']) %
242
+ self._max_seq_num,
243
+ })
244
+
245
+ try:
246
+ cmd = hdr["cmd"]
247
+ if cmd == f"{req_cmd}_ok":
248
+ if depth_flag:
249
+ self._depth_seq = hdr["args"]
250
+ else:
251
+ self._rgb_seq = hdr["args"]
252
+ return hdr, img
253
+ else:
254
+ return None, None
255
+ except KeyError:
256
+ print(f"\033[91m{hdr['cmd']} requires `cmd`\033[0m")
257
+ return None, None
258
+ except Exception as e:
259
+ print(f"\033[91m__process_frame failed: {e}\033[0m")
260
+ return None, None
261
+
262
+ def _get_states_inner(self, robot_name: str | None = None):
263
+ req_cmd = "get_states"
264
+ if robot_name is not None:
265
+ req_cmd += f"_{robot_name}"
266
+ hdr, states = self.request({
267
+ "cmd":
268
+ req_cmd,
269
+ "args": (1 + self._states_seq[robot_name]) % self._max_seq_num,
270
+ })
271
+ try:
272
+ if hdr["cmd"] == f"{req_cmd}_ok":
273
+ self._states_seq[robot_name] = hdr["args"]
274
+ return hdr, states
275
+ else:
276
+ return None, None
277
+ except KeyError:
278
+ print(f"\033[91m{hdr['cmd']} requires `cmd`\033[0m")
279
+ return None, None
280
+ except Exception as e:
281
+ print(f"\033[91m{req_cmd} failed: {e}\033[0m")
282
+ return None, None
283
+
284
+ def _set_cmds_inner(
285
+ self,
286
+ cmds: np.ndarray,
287
+ ) -> bool:
288
+ req_cmd = "set_cmds"
289
+ hdr, _ = self.request(
290
+ {
291
+ "cmd": req_cmd,
292
+ "ts": hex_zmq_ts_now(),
293
+ "args": self._cmds_seq,
294
+ },
295
+ cmds,
296
+ )
297
+ # print(f"{req_cmd} seq: {self._cmds_seq}")
298
+ try:
299
+ cmd = hdr["cmd"]
300
+ if cmd == f"{req_cmd}_ok":
301
+ self._cmds_seq = (self._cmds_seq + 1) % self._max_seq_num
302
+ return True
303
+ else:
304
+ return False
305
+ except KeyError:
306
+ print(f"\033[91m{hdr['cmd']} requires `cmd`\033[0m")
307
+ return False
308
+ except Exception as e:
309
+ print(f"\033[91m{req_cmd} failed: {e}\033[0m")
310
+ return False
311
+
312
+
313
+ class HexMujocoServerBase(HexZMQServerBase):
314
+
315
+ def __init__(self, net_config: dict = NET_CONFIG):
316
+ HexZMQServerBase.__init__(self, net_config)
317
+ self._device: HexDeviceBase = None
318
+ self._states_queue = deque(maxlen=self._deque_maxlen)
319
+ self._obj_pose_queue = deque(maxlen=self._deque_maxlen)
320
+ self._cmds_queue = deque(maxlen=self._deque_maxlen)
321
+ self._cmds_seq = -1
322
+ self._rgb_queue = deque(maxlen=self._deque_maxlen)
323
+ self._depth_queue = deque(maxlen=self._deque_maxlen)
324
+ self._seq_clear_flag = False
325
+
326
+ def work_loop(self):
327
+ try:
328
+ self._device.work_loop([
329
+ self._states_queue,
330
+ self._obj_pose_queue,
331
+ self._cmds_queue,
332
+ self._rgb_queue,
333
+ self._depth_queue,
334
+ self._stop_event,
335
+ ])
336
+ finally:
337
+ self._device.close()
338
+
339
+ def _seq_clear(self):
340
+ self._seq_clear_flag = True
341
+ return True
342
+
343
+ def _get_states(self, recv_hdr: dict):
344
+ try:
345
+ seq = recv_hdr["args"]
346
+ except KeyError:
347
+ print(f"\033[91m{recv_hdr['cmd']} requires `args`\033[0m")
348
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
349
+
350
+ try:
351
+ ts, count, states = self._states_queue[
352
+ -1] if self._realtime_mode else self._states_queue.popleft()
353
+ except IndexError:
354
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
355
+ except Exception as e:
356
+ print(f"\033[91m{recv_hdr['cmd']} failed: {e}\033[0m")
357
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
358
+
359
+ delta = (count - seq) % self._max_seq_num
360
+ if delta >= 0 and delta < 1e6:
361
+ return {
362
+ "cmd": f"{recv_hdr['cmd']}_ok",
363
+ "ts": ts,
364
+ "args": count
365
+ }, states
366
+ else:
367
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
368
+
369
+ def _set_cmds(self, recv_hdr: dict, recv_buf: np.ndarray):
370
+ seq = recv_hdr.get("args", None)
371
+ if self._seq_clear_flag:
372
+ self._seq_clear_flag = False
373
+ self._cmds_seq = -1
374
+ return self.no_ts_hdr(recv_hdr, False), None
375
+
376
+ if seq is not None and seq > self._cmds_seq:
377
+ delta = (seq - self._cmds_seq) % self._max_seq_num
378
+ if delta >= 0 and delta < 1e6:
379
+ self._cmds_seq = seq
380
+ self._cmds_queue.append((recv_hdr["ts"], seq, recv_buf))
381
+ return self.no_ts_hdr(recv_hdr, True), None
382
+ else:
383
+ return self.no_ts_hdr(recv_hdr, False), None
384
+ else:
385
+ return self.no_ts_hdr(recv_hdr, False), None
386
+
387
+ def _get_frame(self, recv_hdr: dict):
388
+ try:
389
+ seq = recv_hdr["args"]
390
+ except KeyError:
391
+ print(f"\033[91m{recv_hdr['cmd']} requires `args`\033[0m")
392
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
393
+
394
+ # get camera config
395
+ split_cmd = recv_hdr["cmd"].split("_")
396
+ depth_flag = split_cmd[1] == "depth"
397
+
398
+ try:
399
+ if depth_flag:
400
+ ts, count, img = self._depth_queue[
401
+ -1] if self._realtime_mode else self._depth_queue.popleft(
402
+ )
403
+ else:
404
+ ts, count, img = self._rgb_queue[
405
+ -1] if self._realtime_mode else self._rgb_queue.popleft()
406
+ except IndexError:
407
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
408
+ except Exception as e:
409
+ print(f"\033[91m{recv_hdr['cmd']} failed: {e}\033[0m")
410
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
411
+
412
+ delta = (count - seq) % self._max_seq_num
413
+ if delta >= 0 and delta < 1e6:
414
+ return {
415
+ "cmd": f"{recv_hdr['cmd']}_ok",
416
+ "ts": ts,
417
+ "args": count
418
+ }, img
419
+ else:
420
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
421
+
422
+ @abstractmethod
423
+ def _process_request(self, recv_hdr: dict, recv_buf: np.ndarray):
424
+ raise NotImplementedError(
425
+ "`_process_request` should be implemented by the child class")
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ ################################################################
4
+ # Copyright 2025 Dong Zhaorui. All rights reserved.
5
+ # Author: Dong Zhaorui 847235539@qq.com
6
+ # Date : 2025-09-12
7
+ ################################################################
8
+
9
+ from .robot_base import HexRobotBase, HexRobotClientBase, HexRobotServerBase
10
+ from .dummy import HexRobotDummy, HexRobotDummyClient, HexRobotDummyServer
11
+ from .gello import HexRobotGello, HexRobotGelloClient, HexRobotGelloServer
12
+ from .hexarm import HexRobotHexarm, HexRobotHexarmClient, HexRobotHexarmServer, HEXARM_URDF_PATH_DICT
13
+
14
+ __all__ = [
15
+ # path
16
+ "HEXARM_URDF_PATH_DICT",
17
+
18
+ # base
19
+ "HexRobotBase",
20
+ "HexRobotClientBase",
21
+ "HexRobotServerBase",
22
+
23
+ # dummy
24
+ "HexRobotDummy",
25
+ "HexRobotDummyClient",
26
+ "HexRobotDummyServer",
27
+
28
+ # gello
29
+ "HexRobotGello",
30
+ "HexRobotGelloClient",
31
+ "HexRobotGelloServer",
32
+
33
+ # hexarm
34
+ "HexRobotHexarm",
35
+ "HexRobotHexarmClient",
36
+ "HexRobotHexarmServer",
37
+ ]
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ ################################################################
4
+ # Copyright 2025 Dong Zhaorui. All rights reserved.
5
+ # Author: Dong Zhaorui 847235539@qq.com
6
+ # Date : 2025-09-12
7
+ ################################################################
8
+
9
+ from .robot_dummy import HexRobotDummy
10
+ from .robot_dummy_cli import HexRobotDummyClient
11
+ from .robot_dummy_srv import HexRobotDummyServer
12
+
13
+ __all__ = [
14
+ "HexRobotDummy",
15
+ "HexRobotDummyClient",
16
+ "HexRobotDummyServer",
17
+ ]
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ ################################################################
4
+ # Copyright 2025 Dong Zhaorui. All rights reserved.
5
+ # Author: Dong Zhaorui 847235539@qq.com
6
+ # Date : 2025-09-12
7
+ ################################################################
8
+
9
+ import threading
10
+ import numpy as np
11
+ from collections import deque
12
+
13
+ from ..robot_base import HexRobotBase
14
+ from ...zmq_base import (
15
+ hex_zmq_ts_now,
16
+ hex_zmq_ts_delta_ms,
17
+ HexRate,
18
+ )
19
+ from ...hex_launch import hex_log, HEX_LOG_LEVEL
20
+
21
+ ROBOT_CONFIG = {
22
+ "dofs": [7],
23
+ "limits": [[[-1.0, 1.0]] * 3] * 7,
24
+ "states_init": [[0.0, 0.0, 0.0]] * 7,
25
+ }
26
+
27
+
28
+ class HexRobotDummy(HexRobotBase):
29
+
30
+ def __init__(
31
+ self,
32
+ robot_config: dict = ROBOT_CONFIG,
33
+ realtime_mode: bool = False,
34
+ ):
35
+ HexRobotBase.__init__(self, realtime_mode)
36
+
37
+ try:
38
+ self._dofs = robot_config["dofs"]
39
+ self._limits = np.array(robot_config["limits"])
40
+ except KeyError as ke:
41
+ missing_key = ke.args[0]
42
+ raise ValueError(
43
+ f"robot_config is not valid, missing key: {missing_key}")
44
+
45
+ # start work loop
46
+ self._working.set()
47
+
48
+ def __del__(self):
49
+ HexRobotBase.__del__(self)
50
+
51
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
52
+ states_queue = hex_queues[0]
53
+ cmds_queue = hex_queues[1]
54
+ stop_event = hex_queues[2]
55
+
56
+ dummy_states = np.zeros((self._dofs[0], 3))
57
+ states_count = 0
58
+ last_cmds_seq = -1
59
+ rate = HexRate(1000)
60
+ while self._working.is_set() and not stop_event.is_set():
61
+ # states
62
+ states_queue.append((hex_zmq_ts_now(), states_count, dummy_states))
63
+ states_count = (states_count + 1) % self._max_seq_num
64
+
65
+ # cmds
66
+ cmds_pack = None
67
+ try:
68
+ cmds_pack = cmds_queue[
69
+ -1] if self._realtime_mode else cmds_queue.popleft()
70
+ except IndexError:
71
+ pass
72
+ if cmds_pack is not None:
73
+ ts, seq, cmds = cmds_pack
74
+ if seq != last_cmds_seq:
75
+ last_cmds_seq = seq
76
+ if hex_zmq_ts_delta_ms(hex_zmq_ts_now(), ts) < 200.0:
77
+ cmds = np.clip(
78
+ cmds,
79
+ self._limits[:, :, 0],
80
+ self._limits[:, :, 1],
81
+ )
82
+ dummy_states = cmds.copy()
83
+
84
+ # sleep
85
+ rate.sleep()
86
+
87
+ # close
88
+ self.close()
89
+
90
+ def close(self):
91
+ if not self._working.is_set():
92
+ return
93
+ self._working.clear()
94
+ hex_log(HEX_LOG_LEVEL["info"], "HexRobotDummy closed")
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ ################################################################
4
+ # Copyright 2025 Dong Zhaorui. All rights reserved.
5
+ # Author: Dong Zhaorui 847235539@qq.com
6
+ # Date : 2025-09-16
7
+ ################################################################
8
+
9
+ from ..robot_base import HexRobotClientBase
10
+
11
+ NET_CONFIG = {
12
+ "ip": "127.0.0.1",
13
+ "port": 12345,
14
+ "realtime_mode": False,
15
+ "deque_maxlen": 10,
16
+ "client_timeout_ms": 200,
17
+ "server_timeout_ms": 1_000,
18
+ "server_num_workers": 4,
19
+ }
20
+
21
+
22
+ class HexRobotDummyClient(HexRobotClientBase):
23
+
24
+ def __init__(
25
+ self,
26
+ net_config: dict = NET_CONFIG,
27
+ ):
28
+ HexRobotClientBase.__init__(self, net_config)
29
+ self._wait_for_working()
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ ################################################################
4
+ # Copyright 2025 Dong Zhaorui. All rights reserved.
5
+ # Author: Dong Zhaorui 847235539@qq.com
6
+ # Date : 2025-09-12
7
+ ################################################################
8
+
9
+ import numpy as np
10
+
11
+ try:
12
+ from ..robot_base import HexRobotServerBase
13
+ from .robot_dummy import HexRobotDummy
14
+ except (ImportError, ValueError):
15
+ import sys
16
+ from pathlib import Path
17
+ this_file = Path(__file__).resolve()
18
+ project_root = this_file.parents[3]
19
+ if str(project_root) not in sys.path:
20
+ sys.path.insert(0, str(project_root))
21
+ from hex_zmq_servers.robot.robot_base import HexRobotServerBase
22
+ from hex_zmq_servers.robot.dummy.robot_dummy import HexRobotDummy
23
+
24
+ NET_CONFIG = {
25
+ "ip": "127.0.0.1",
26
+ "port": 12345,
27
+ "realtime_mode": False,
28
+ "deque_maxlen": 10,
29
+ "client_timeout_ms": 200,
30
+ "server_timeout_ms": 1_000,
31
+ "server_num_workers": 4,
32
+ }
33
+
34
+ ROBOT_CONFIG = {
35
+ "dofs": [7],
36
+ "limits": [[[-1.0, 1.0]] * 3] * 7,
37
+ "states_init": [[0.0, 0.0, 0.0]] * 7,
38
+ }
39
+
40
+
41
+ class HexRobotDummyServer(HexRobotServerBase):
42
+
43
+ def __init__(
44
+ self,
45
+ net_config: dict = NET_CONFIG,
46
+ params_config: dict = ROBOT_CONFIG,
47
+ ):
48
+ HexRobotServerBase.__init__(self, net_config)
49
+
50
+ # robot
51
+ self._device = HexRobotDummy(params_config,
52
+ net_config.get("realtime_mode", False))
53
+
54
+ def _process_request(self, recv_hdr: dict, recv_buf: np.ndarray):
55
+ if recv_hdr["cmd"] == "is_working":
56
+ return self.no_ts_hdr(recv_hdr, self._device.is_working()), None
57
+ elif recv_hdr["cmd"] == "seq_clear":
58
+ return self.no_ts_hdr(recv_hdr, self._seq_clear()), None
59
+ elif recv_hdr["cmd"] == "get_dofs":
60
+ dofs = self._device.get_dofs()
61
+ return self.no_ts_hdr(recv_hdr, dofs is not None), dofs
62
+ elif recv_hdr["cmd"] == "get_limits":
63
+ limits = self._device.get_limits()
64
+ return self.no_ts_hdr(recv_hdr, limits is not None), limits
65
+ elif recv_hdr["cmd"] == "get_states":
66
+ return self._get_states(recv_hdr)
67
+ elif recv_hdr["cmd"] == "set_cmds":
68
+ return self._set_cmds(recv_hdr, recv_buf)
69
+ else:
70
+ raise ValueError(f"unknown command: {recv_hdr['cmd']}")
71
+
72
+
73
+ if __name__ == "__main__":
74
+ import argparse, json
75
+ from hex_zmq_servers.zmq_base import hex_server_helper
76
+
77
+ parser = argparse.ArgumentParser()
78
+ parser.add_argument("--cfg", type=str, required=True)
79
+ args = parser.parse_args()
80
+ cfg = json.loads(args.cfg)
81
+
82
+ hex_server_helper(cfg, HexRobotDummyServer)
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding:utf-8 -*-
3
+ ################################################################
4
+ # Copyright 2025 Dong Zhaorui. All rights reserved.
5
+ # Author: Dong Zhaorui 847235539@qq.com
6
+ # Date : 2025-09-12
7
+ ################################################################
8
+
9
+ from .robot_gello import HexRobotGello
10
+ from .robot_gello_cli import HexRobotGelloClient
11
+ from .robot_gello_srv import HexRobotGelloServer
12
+
13
+ __all__ = [
14
+ "HexRobotGello",
15
+ "HexRobotGelloClient",
16
+ "HexRobotGelloServer",
17
+ ]