hex-zmq-servers 0.3.2__py3-none-any.whl → 0.3.4__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 (45) hide show
  1. hex_zmq_servers/__init__.py +45 -20
  2. hex_zmq_servers/cam/__init__.py +29 -5
  3. hex_zmq_servers/cam/berxel/cam_berxel.py +8 -10
  4. hex_zmq_servers/cam/berxel/cam_berxel_cli.py +1 -0
  5. hex_zmq_servers/cam/cam_base.py +40 -10
  6. hex_zmq_servers/cam/dummy/cam_dummy.py +7 -7
  7. hex_zmq_servers/cam/dummy/cam_dummy_cli.py +1 -0
  8. hex_zmq_servers/cam/realsense/__init__.py +17 -0
  9. hex_zmq_servers/cam/realsense/cam_realsense.py +157 -0
  10. hex_zmq_servers/cam/realsense/cam_realsense_cli.py +31 -0
  11. hex_zmq_servers/cam/realsense/cam_realsense_srv.py +75 -0
  12. hex_zmq_servers/cam/rgb/__init__.py +17 -0
  13. hex_zmq_servers/cam/rgb/cam_rgb.py +133 -0
  14. hex_zmq_servers/cam/rgb/cam_rgb_cli.py +41 -0
  15. hex_zmq_servers/cam/rgb/cam_rgb_srv.py +75 -0
  16. hex_zmq_servers/config/cam_realsense.json +15 -0
  17. hex_zmq_servers/config/cam_rgb.json +26 -0
  18. hex_zmq_servers/config/mujoco_archer_y6.json +2 -1
  19. hex_zmq_servers/config/mujoco_e3_desktop.json +6 -1
  20. hex_zmq_servers/config/robot_hexarm.json +1 -1
  21. hex_zmq_servers/device_base.py +3 -2
  22. hex_zmq_servers/mujoco/archer_y6/model/robot.xml +6 -6
  23. hex_zmq_servers/mujoco/archer_y6/model/scene.xml +1 -1
  24. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6.py +74 -39
  25. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_cli.py +42 -0
  26. hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_srv.py +16 -14
  27. hex_zmq_servers/mujoco/e3_desktop/model/robot.xml +12 -12
  28. hex_zmq_servers/mujoco/e3_desktop/model/scene.xml +1 -1
  29. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop.py +138 -70
  30. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_cli.py +148 -33
  31. hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_srv.py +39 -35
  32. hex_zmq_servers/mujoco/mujoco_base.py +101 -64
  33. hex_zmq_servers/robot/dummy/robot_dummy.py +11 -7
  34. hex_zmq_servers/robot/dummy/robot_dummy_cli.py +1 -0
  35. hex_zmq_servers/robot/gello/robot_gello.py +11 -7
  36. hex_zmq_servers/robot/gello/robot_gello_cli.py +1 -0
  37. hex_zmq_servers/robot/hexarm/robot_hexarm.py +56 -22
  38. hex_zmq_servers/robot/hexarm/robot_hexarm_cli.py +1 -0
  39. hex_zmq_servers/robot/robot_base.py +40 -10
  40. hex_zmq_servers/zmq_base.py +97 -33
  41. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/METADATA +7 -6
  42. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/RECORD +45 -35
  43. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/WHEEL +0 -0
  44. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/licenses/LICENSE +0 -0
  45. {hex_zmq_servers-0.3.2.dist-info → hex_zmq_servers-0.3.4.dist-info}/top_level.txt +0 -0
@@ -11,13 +11,13 @@ import subprocess
11
11
  import time
12
12
  import threading
13
13
  import numpy as np
14
+ from collections import deque
14
15
 
15
16
  from ..robot_base import HexRobotBase
16
17
  from ...zmq_base import (
17
18
  hex_zmq_ts_now,
18
19
  hex_zmq_ts_delta_ms,
19
20
  HexRate,
20
- HexSafeValue,
21
21
  )
22
22
  from ...hex_launch import hex_log, HEX_LOG_LEVEL
23
23
  from dynamixel_sdk.group_sync_read import GroupSyncRead
@@ -104,10 +104,10 @@ class HexRobotGello(HexRobotBase):
104
104
  # start work loop
105
105
  self._working.set()
106
106
 
107
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
108
- states_value = hex_values[0]
109
- cmds_value = hex_values[1]
110
- stop_event = hex_values[2]
107
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
108
+ states_queue = hex_queues[0]
109
+ cmds_queue = hex_queues[1]
110
+ stop_event = hex_queues[2]
111
111
 
112
112
  states_count = 0
113
113
  last_cmds_seq = -1
@@ -116,11 +116,15 @@ class HexRobotGello(HexRobotBase):
116
116
  # states
117
117
  ts, states = self.__get_states()
118
118
  if states is not None:
119
- states_value.set((ts, states_count, states))
119
+ states_queue.append((ts, states_count, states))
120
120
  states_count = (states_count + 1) % self._max_seq_num
121
121
 
122
122
  # cmds
123
- cmds_pack = cmds_value.get(timeout_s=-1.0)
123
+ cmds_pack = None
124
+ try:
125
+ cmds_pack = cmds_queue.popleft()
126
+ except IndexError:
127
+ pass
124
128
  if cmds_pack is not None:
125
129
  ts, seq, cmds = cmds_pack
126
130
  if seq != last_cmds_seq:
@@ -24,3 +24,4 @@ class HexRobotGelloClient(HexRobotClientBase):
24
24
  net_config: dict = NET_CONFIG,
25
25
  ):
26
26
  HexRobotClientBase.__init__(self, net_config)
27
+ self._wait_for_working()
@@ -9,13 +9,13 @@
9
9
  import time
10
10
  import threading
11
11
  import numpy as np
12
+ from collections import deque
12
13
 
13
14
  from ..robot_base import HexRobotBase
14
15
  from ...zmq_base import (
15
16
  hex_zmq_ts_now,
16
17
  hex_zmq_ts_delta_ms,
17
18
  HexRate,
18
- HexSafeValue,
19
19
  )
20
20
  from ...hex_launch import hex_log, HEX_LOG_LEVEL
21
21
  from hex_device import HexDeviceApi, MotorBase
@@ -73,6 +73,10 @@ class HexRobotHexarm(HexRobotBase):
73
73
  self.__arm_archer: MotorBase | None = None
74
74
  self.__gripper: MotorBase | None = None
75
75
 
76
+ # buffer
77
+ self.__arm_state_buffer: dict | None = None
78
+ self.__gripper_state_buffer: dict | None = None
79
+
76
80
  # open device
77
81
  self.__hex_api = HexDeviceApi(
78
82
  ws_url=f"ws://{device_ip}:{device_port}",
@@ -122,26 +126,30 @@ class HexRobotHexarm(HexRobotBase):
122
126
  # start work loop
123
127
  self._working.set()
124
128
 
125
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
126
- states_value = hex_values[0]
127
- cmds_value = hex_values[1]
128
- stop_event = hex_values[2]
129
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
130
+ states_queue = hex_queues[0]
131
+ cmds_queue = hex_queues[1]
132
+ stop_event = hex_queues[2]
129
133
 
130
134
  last_states_ts = hex_zmq_ts_now()
131
135
  states_count = 0
132
136
  last_cmds_seq = -1
133
- rate = HexRate(1000)
137
+ rate = HexRate(2000)
134
138
  while self._working.is_set() and not stop_event.is_set():
135
139
  # states
136
140
  ts, states = self.__get_states()
137
141
  if states is not None:
138
- if hex_zmq_ts_delta_ms(ts, last_states_ts) > 1.0:
142
+ if hex_zmq_ts_delta_ms(ts, last_states_ts) > 1e-6:
139
143
  last_states_ts = ts
140
- states_value.set((ts, states_count, states))
144
+ states_queue.append((ts, states_count, states))
141
145
  states_count = (states_count + 1) % self._max_seq_num
142
146
 
143
147
  # cmds
144
- cmds_pack = cmds_value.get(timeout_s=-1.0)
148
+ cmds_pack = None
149
+ try:
150
+ cmds_pack = cmds_queue.popleft()
151
+ except IndexError:
152
+ pass
145
153
  if cmds_pack is not None:
146
154
  ts, seq, cmds = cmds_pack
147
155
  if seq != last_cmds_seq:
@@ -160,22 +168,47 @@ class HexRobotHexarm(HexRobotBase):
160
168
  return None, None
161
169
 
162
170
  # (arm_dofs, 3) # pos vel eff
163
- arm_states_dict = self.__arm_archer.get_simple_motor_status()
164
- pos = arm_states_dict['pos']
165
- vel = arm_states_dict['vel']
166
- eff = arm_states_dict['eff']
167
- ts = arm_states_dict['ts']
171
+ if self.__arm_state_buffer is None:
172
+ self.__arm_state_buffer = self.__arm_archer.get_simple_motor_status(
173
+ )
168
174
 
169
175
  # (gripper_dofs, 3) # pos vel eff
170
- if self.__gripper is not None:
171
- gripper_states_dict = self.__gripper.get_simple_motor_status()
172
- pos += gripper_states_dict['pos']
173
- vel += gripper_states_dict['vel']
174
- eff += gripper_states_dict['eff']
176
+ if self.__gripper is not None and self.__gripper_state_buffer is None:
177
+ self.__gripper_state_buffer = self.__gripper.get_simple_motor_status(
178
+ )
179
+
180
+ arm_ready = self.__arm_state_buffer is not None
181
+ gripper_ready = self.__gripper is None or self.__gripper_state_buffer is not None
182
+ if arm_ready and gripper_ready:
183
+ arm_ts = self.__arm_state_buffer['ts']
184
+ gripper_ts = self.__gripper_state_buffer[
185
+ 'ts'] if self.__gripper is not None else arm_ts
186
+
187
+ delta_ms = hex_zmq_ts_delta_ms(arm_ts, gripper_ts)
188
+ if np.fabs(delta_ms) < 1e-6:
189
+ pos = self.__arm_state_buffer['pos']
190
+ vel = self.__arm_state_buffer['vel']
191
+ eff = self.__arm_state_buffer['eff']
192
+
193
+ if self.__gripper is not None:
194
+ pos = np.concatenate(
195
+ [pos, self.__gripper_state_buffer['pos']])
196
+ vel = np.concatenate(
197
+ [vel, self.__gripper_state_buffer['vel']])
198
+ eff = np.concatenate(
199
+ [eff, self.__gripper_state_buffer['eff']])
200
+
201
+ state = np.array([pos, vel, eff]).T
202
+ self.__arm_state_buffer, self.__gripper_state_buffer = None, None
203
+ return arm_ts if self.__sens_ts else hex_zmq_ts_now(), state
204
+ elif delta_ms > 0.0:
205
+ self.__gripper_state_buffer = None
206
+ return None, None
207
+ else:
208
+ self.__arm_state_buffer = None
209
+ return None, None
175
210
 
176
- pos, vel, eff = np.asarray(pos), np.asarray(vel), np.asarray(eff)
177
- return ts if self.__sens_ts else hex_zmq_ts_now(), np.array(
178
- [pos, vel, eff]).T
211
+ return None, None
179
212
 
180
213
  def __set_cmds(self, cmds: np.ndarray) -> bool:
181
214
  # cmds: (n)
@@ -254,5 +287,6 @@ class HexRobotHexarm(HexRobotBase):
254
287
  return
255
288
  self._working.clear()
256
289
  self.__arm_archer.stop()
290
+ time.sleep(0.5)
257
291
  self.__hex_api.close()
258
292
  hex_log(HEX_LOG_LEVEL["info"], "HexRobotHexarm closed")
@@ -32,3 +32,4 @@ class HexRobotHexarmClient(HexRobotClientBase):
32
32
  net_config: dict = NET_CONFIG,
33
33
  ):
34
34
  HexRobotClientBase.__init__(self, net_config)
35
+ self._wait_for_working()
@@ -8,12 +8,13 @@
8
8
 
9
9
  import threading
10
10
  import numpy as np
11
+ from collections import deque
11
12
  from abc import abstractmethod
12
13
 
13
14
  from ..device_base import HexDeviceBase
14
15
  from ..zmq_base import (
15
16
  hex_zmq_ts_now,
16
- HexSafeValue,
17
+ HexRate,
17
18
  HexZMQClientBase,
18
19
  HexZMQServerBase,
19
20
  )
@@ -82,7 +83,7 @@ class HexRobotBase(HexDeviceBase):
82
83
  return normed_rads
83
84
 
84
85
  @abstractmethod
85
- def work_loop(self, hex_values: list[HexSafeValue | threading.Event]):
86
+ def work_loop(self, hex_queues: list[deque | threading.Event]):
86
87
  raise NotImplementedError(
87
88
  "`work_loop` should be implemented by the child class")
88
89
 
@@ -98,6 +99,8 @@ class HexRobotClientBase(HexZMQClientBase):
98
99
  HexZMQClientBase.__init__(self, net_config)
99
100
  self._states_seq = 0
100
101
  self._cmds_seq = 0
102
+ self._states_queue = deque(maxlen=10)
103
+ self._cmds_queue = deque(maxlen=10)
101
104
 
102
105
  def __del__(self):
103
106
  HexZMQClientBase.__del__(self)
@@ -114,7 +117,17 @@ class HexRobotClientBase(HexZMQClientBase):
114
117
  _, limits = self.request({"cmd": "get_limits"})
115
118
  return limits
116
119
 
117
- def get_states(self):
120
+ def get_states(self, newest: bool = False):
121
+ try:
122
+ return self._states_queue.popleft(
123
+ ) if not newest else self._states_queue[-1]
124
+ except IndexError:
125
+ return None, None
126
+
127
+ def set_cmds(self, cmds: np.ndarray):
128
+ self._cmds_queue.append(cmds)
129
+
130
+ def _get_states_inner(self):
118
131
  hdr, states = self.request({
119
132
  "cmd":
120
133
  "get_states",
@@ -134,7 +147,7 @@ class HexRobotClientBase(HexZMQClientBase):
134
147
  print(f"\033[91mget_states failed: {e}\033[0m")
135
148
  return None, None
136
149
 
137
- def set_cmds(self, cmds: np.ndarray) -> bool:
150
+ def _set_cmds_inner(self, cmds: np.ndarray) -> bool:
138
151
  hdr, _ = self.request(
139
152
  {
140
153
  "cmd": "set_cmds",
@@ -158,14 +171,29 @@ class HexRobotClientBase(HexZMQClientBase):
158
171
  print(f"\033[91mset_cmds failed: {e}\033[0m")
159
172
  return False
160
173
 
174
+ def _recv_loop(self):
175
+ rate = HexRate(2000)
176
+ while self._recv_flag:
177
+ hdr, states = self._get_states_inner()
178
+ if hdr is not None:
179
+ self._states_queue.append((hdr, states))
180
+
181
+ try:
182
+ cmds = self._cmds_queue.popleft()
183
+ _ = self._set_cmds_inner(cmds)
184
+ except IndexError:
185
+ pass
186
+
187
+ rate.sleep()
188
+
161
189
 
162
190
  class HexRobotServerBase(HexZMQServerBase):
163
191
 
164
192
  def __init__(self, net_config: dict = NET_CONFIG):
165
193
  HexZMQServerBase.__init__(self, net_config)
166
194
  self._device: HexDeviceBase = None
167
- self._states_value = HexSafeValue()
168
- self._cmds_value = HexSafeValue()
195
+ self._states_queue = deque(maxlen=10)
196
+ self._cmds_queue = deque(maxlen=10)
169
197
  self._cmds_seq = -1
170
198
  self._seq_clear_flag = False
171
199
 
@@ -176,8 +204,8 @@ class HexRobotServerBase(HexZMQServerBase):
176
204
  def work_loop(self):
177
205
  try:
178
206
  self._device.work_loop([
179
- self._states_value,
180
- self._cmds_value,
207
+ self._states_queue,
208
+ self._cmds_queue,
181
209
  self._stop_event,
182
210
  ])
183
211
  finally:
@@ -195,7 +223,9 @@ class HexRobotServerBase(HexZMQServerBase):
195
223
  return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
196
224
 
197
225
  try:
198
- ts, count, states = self._states_value.get()
226
+ ts, count, states = self._states_queue.popleft()
227
+ except IndexError:
228
+ return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
199
229
  except Exception as e:
200
230
  print(f"\033[91m{recv_hdr['cmd']} failed: {e}\033[0m")
201
231
  return {"cmd": f"{recv_hdr['cmd']}_failed"}, None
@@ -221,7 +251,7 @@ class HexRobotServerBase(HexZMQServerBase):
221
251
  delta = (seq - self._cmds_seq) % self._max_seq_num
222
252
  if delta >= 0 and delta < 1e6:
223
253
  self._cmds_seq = seq
224
- self._cmds_value.set((recv_hdr["ts"], seq, recv_buf))
254
+ self._cmds_queue.append((recv_hdr["ts"], seq, recv_buf))
225
255
  return self.no_ts_hdr(recv_hdr, True), None
226
256
  else:
227
257
  return self.no_ts_hdr(recv_hdr, False), None
@@ -6,11 +6,10 @@
6
6
  # Date : 2025-09-12
7
7
  ################################################################
8
8
 
9
- import os, signal
10
- import time
11
- import zmq
9
+ import os, signal, json
10
+ import time, ctypes, ctypes.util
12
11
  import threading
13
- import json
12
+ import zmq
14
13
  import numpy as np
15
14
  from abc import ABC, abstractmethod
16
15
 
@@ -21,14 +20,73 @@ MAX_SEQ_NUM = int(1e12)
21
20
  ################################################################
22
21
 
23
22
 
24
- def hex_zmq_ts_now() -> dict:
25
- t_ns = time.time_ns()
23
+ class SingletonMeta(type):
24
+ _instances = {}
25
+
26
+ def __call__(cls, *args, **kwargs):
27
+ if cls not in cls._instances:
28
+ cls._instances[cls] = super().__call__(*args, **kwargs)
29
+ return cls._instances[cls]
30
+
31
+
32
+ class HexTimeManager(metaclass=SingletonMeta):
33
+
34
+ class timespec(ctypes.Structure):
35
+ _fields_ = [("tv_sec", ctypes.c_long), ("tv_nsec", ctypes.c_long)]
36
+
37
+ def __init__(self):
38
+ self.__use_ptp = False
39
+ ptp_path = os.getenv("HEX_PTP_CLOCK", None)
40
+ if ptp_path is not None:
41
+ self.__fd = os.open(ptp_path, os.O_RDONLY | os.O_CLOEXEC)
42
+ self.__clock_id = ((~self.__fd) << 3) | 3
43
+ self.__libc = ctypes.CDLL(
44
+ ctypes.util.find_library("c"),
45
+ use_errno=True,
46
+ )
47
+ self.__use_ptp = True
48
+ print(f"Using PTP clock from {ptp_path}")
49
+ else:
50
+ print("Using system clock")
51
+
52
+ def __del__(self):
53
+ if self.__use_ptp:
54
+ os.close(self.__fd)
55
+
56
+ def get_now_ns(self) -> int:
57
+ if self.__use_ptp:
58
+ ts = self.timespec()
59
+ if self.__libc.clock_gettime(self.__clock_id,
60
+ ctypes.byref(ts)) != 0:
61
+ err = ctypes.get_errno()
62
+ raise OSError(err, os.strerror(err))
63
+ return ts.tv_sec * 1_000_000_000 + ts.tv_nsec
64
+ else:
65
+ return time.perf_counter_ns()
66
+
67
+
68
+ _HEX_TIME_MANAGER = HexTimeManager()
69
+
70
+
71
+ def hex_zmq_ts_to_ns(ts: dict) -> int:
72
+ return ts['s'] * 1_000_000_000 + ts['ns']
73
+
74
+
75
+ def ns_to_hex_zmq_ts(ns: int) -> dict:
26
76
  return {
27
- "s": t_ns // 1_000_000_000,
28
- "ns": t_ns % 1_000_000_000,
77
+ "s": ns // 1_000_000_000,
78
+ "ns": ns % 1_000_000_000,
29
79
  }
30
80
 
31
81
 
82
+ def hex_ns_now() -> int:
83
+ return _HEX_TIME_MANAGER.get_now_ns()
84
+
85
+
86
+ def hex_zmq_ts_now() -> dict:
87
+ return ns_to_hex_zmq_ts(hex_ns_now())
88
+
89
+
32
90
  def hex_zmq_ts_delta_ms(curr_ts, hdr_ts) -> float:
33
91
  try:
34
92
  return (curr_ts['s'] - hdr_ts['s']) * 1_000 + (
@@ -52,7 +110,7 @@ class HexRate:
52
110
 
53
111
  @staticmethod
54
112
  def __now_ns() -> int:
55
- return time.perf_counter_ns()
113
+ return hex_ns_now()
56
114
 
57
115
  def reset(self):
58
116
  self.__next_ns = self.__now_ns() + self.__period_ns
@@ -85,28 +143,6 @@ class HexRate:
85
143
  # ZMQ Related
86
144
  ################################################################
87
145
 
88
-
89
- class HexSafeValue:
90
-
91
- def __init__(self):
92
- self.__value = None
93
- self.__ready = threading.Event()
94
- self.__lock = threading.Lock()
95
-
96
- def set(self, value):
97
- with self.__lock:
98
- self.__value = value
99
- self.__ready.set()
100
-
101
- def get(self, timeout_s=1.0):
102
- if (not self.__ready.is_set()) and timeout_s > 0.0:
103
- print(f"no value yet, waiting for {timeout_s}s")
104
- self.__ready.wait(timeout_s)
105
-
106
- with self.__lock:
107
- return self.__value
108
-
109
-
110
146
  NET_CONFIG = {
111
147
  "ip": "127.0.0.1",
112
148
  "port": 12345,
@@ -137,6 +173,13 @@ class HexZMQClientBase(ABC):
137
173
  self._lock = threading.Lock()
138
174
  self.__make_socket()
139
175
 
176
+ # receive thread
177
+ self._recv_thread = threading.Thread(
178
+ target=self._recv_loop,
179
+ daemon=True,
180
+ )
181
+ self._recv_flag = False
182
+
140
183
  def __del__(self):
141
184
  self.close()
142
185
 
@@ -171,9 +214,12 @@ class HexZMQClientBase(ABC):
171
214
  self.__make_socket()
172
215
  return resp_hdr, resp_buf
173
216
 
174
- def is_working(self):
217
+ def is_working(self) -> bool:
175
218
  working_hdr, _ = self.request({"cmd": "is_working"})
176
- return working_hdr
219
+ if working_hdr is None:
220
+ return False
221
+ else:
222
+ return working_hdr["cmd"] == "is_working_ok"
177
223
 
178
224
  def __send_req(self, req_dict: dict, req_buf: np.ndarray | None = None):
179
225
  # construct send header
@@ -220,12 +266,30 @@ class HexZMQClientBase(ABC):
220
266
  return None, None
221
267
 
222
268
  def close(self):
269
+ self._recv_flag = False
270
+ self._recv_thread.join()
223
271
  if self._socket is not None:
224
272
  try:
225
273
  self._socket.close(0)
226
274
  except Exception:
227
275
  pass
228
276
 
277
+ def _wait_for_working(self, timeout: float = 5.0):
278
+ for _ in range(int(timeout * 10)):
279
+ if self.is_working():
280
+ if hasattr(self, "seq_clear"):
281
+ self.seq_clear()
282
+ break
283
+ else:
284
+ time.sleep(0.1)
285
+ self._recv_flag = True
286
+ self._recv_thread.start()
287
+
288
+ @abstractmethod
289
+ def _recv_loop(self):
290
+ raise NotImplementedError(
291
+ "`_receive_thread` should be implemented by the child class")
292
+
229
293
 
230
294
  class HexZMQServerBase(ABC):
231
295
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hex_zmq_servers
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: HEXFELLOW ZMQ Servers
5
5
  Author-email: Dong Zhaorui <joray.dong@hexfellow.com>
6
6
  Maintainer-email: jecjune <zejun.chen@hexfellow.com>, Dong Zhaorui <joray.dong@hexfellow.com>
@@ -26,16 +26,17 @@ Requires-Dist: pyzmq>=27.0.1
26
26
  Requires-Dist: hex_device<1.4.0,>=1.3.1
27
27
  Requires-Dist: hex_robo_utils<0.3.0,>=0.2.0
28
28
  Requires-Dist: dynamixel-sdk==3.8.4
29
- Provides-Extra: camera
30
- Requires-Dist: berxel_py_wrapper>=2.0.182; extra == "camera"
31
- Requires-Dist: opencv-python>=4.2; extra == "camera"
29
+ Requires-Dist: opencv-python>=4.2
30
+ Provides-Extra: berxel
31
+ Requires-Dist: berxel_py_wrapper>=2.0.182; extra == "berxel"
32
+ Provides-Extra: realsense
33
+ Requires-Dist: pyrealsense2>=2.56.5.9235; extra == "realsense"
32
34
  Provides-Extra: mujoco
33
35
  Requires-Dist: mujoco>=3.3.3; extra == "mujoco"
34
- Requires-Dist: opencv-python>=4.2; extra == "mujoco"
35
36
  Provides-Extra: all
36
37
  Requires-Dist: berxel_py_wrapper>=2.0.182; extra == "all"
37
38
  Requires-Dist: mujoco>=3.3.3; extra == "all"
38
- Requires-Dist: opencv-python>=4.2; extra == "all"
39
+ Requires-Dist: pyrealsense2>=2.56.5.9235; extra == "all"
39
40
  Dynamic: license-file
40
41
 
41
42
  # hex_zmq_servers
@@ -1,33 +1,43 @@
1
- hex_zmq_servers/__init__.py,sha256=1tHkUyB8TBlnCb54C9jroTYAaIkPmn8ZeHB_v6HQEb0,4702
2
- hex_zmq_servers/device_base.py,sha256=NGVQvLZFw3l2ZC55tHuKHLVXppEoYHt1eK_q-zm5eGA,1182
1
+ hex_zmq_servers/__init__.py,sha256=1dnB9YTRq5uAFHo_3dMp1t1UxS4ZrcuZ_7CzBwqAfEM,5426
2
+ hex_zmq_servers/device_base.py,sha256=dABpeUg84Au-vtbVzvnv1Tmn_898CNOkIvH2UDUnV3I,1191
3
3
  hex_zmq_servers/hex_launch.py,sha256=opS_WwntAzV3UjOCVBo7FaLVW_dP3jlO40q-gFSBy68,17543
4
- hex_zmq_servers/zmq_base.py,sha256=BO1uoldQ9g5zo7S5ZgUF2tSs8SKx1X6aWJg_moeav3k,14356
5
- hex_zmq_servers/cam/__init__.py,sha256=qKKr4UVy7qdCxRP_q_9ZO6MYrLn5WokofTUDMwYAitI,761
6
- hex_zmq_servers/cam/cam_base.py,sha256=QtiuaXRrg1bhFDgwnKAaVVWxhvRpulNc_4rB3i_696Q,4110
4
+ hex_zmq_servers/zmq_base.py,sha256=RFRvl95oci6d-urP5n-qGDh5090pZ5q7Oy8_vNPqJxI,16380
5
+ hex_zmq_servers/cam/__init__.py,sha256=hZ4NTEaO9uxcBua-nyJTpXxmk_GiIAcK2uWz3QQbwvE,1408
6
+ hex_zmq_servers/cam/cam_base.py,sha256=3INQUYzzue4B0EiLEuo8IZDfLTifjmqXpXIkPaNA7dY,5134
7
7
  hex_zmq_servers/cam/berxel/__init__.py,sha256=3fK06RWAS_q8a_3z-RxFTv32e99og-4dMZTxH08TOcY,508
8
- hex_zmq_servers/cam/berxel/cam_berxel.py,sha256=emy6lFcJOPsKp0hW0GGuXV6wOx0GT0Ihyn3d9CQu3pw,9033
9
- hex_zmq_servers/cam/berxel/cam_berxel_cli.py,sha256=oWRiKJIbVBF8e3cKROlTUj4l11IPBWa4mg6oMiwE7aw,778
8
+ hex_zmq_servers/cam/berxel/cam_berxel.py,sha256=l7SGnMUsM4D8HqHrJM9_QSyvLeNSRSDb37-_tSNHF-c,9035
9
+ hex_zmq_servers/cam/berxel/cam_berxel_cli.py,sha256=ZbtZ4fjO0MCjqstfCxJwH1sMAtHKFKFIg9QI1zxplMQ,811
10
10
  hex_zmq_servers/cam/berxel/cam_berxel_srv.py,sha256=8QPhPpRsYnbbIFTQAT3sZA1nRTq1XpKg-GmD6ERUu9w,2311
11
11
  hex_zmq_servers/cam/dummy/__init__.py,sha256=EziQ8H8d9Vm1AYw4YryIumTXpx-lwEnlfzTl7VAnKHM,499
12
- hex_zmq_servers/cam/dummy/cam_dummy.py,sha256=HjQG_uiAXLxRv3FucitH23XLAv1PbPvsDNWu1QgtFoE,1887
13
- hex_zmq_servers/cam/dummy/cam_dummy_cli.py,sha256=PJoQQBk0I3GUrdxUe0FfFKRfQINNoE_pnNUmaWvCoVs,657
12
+ hex_zmq_servers/cam/dummy/cam_dummy.py,sha256=pWw0ceVCcKS9BJdD3JjGAAtSc1sqmYsL2UyGhmwVI7M,1898
13
+ hex_zmq_servers/cam/dummy/cam_dummy_cli.py,sha256=kA5eJiF4VRcOTFFB0rdtXBT0SuT2fb9GMK7J8vGkafw,690
14
14
  hex_zmq_servers/cam/dummy/cam_dummy_srv.py,sha256=rUcTA00cSi3Hju1NrT8dp-8WRp7rYcREgVRESC8na0U,1986
15
+ hex_zmq_servers/cam/realsense/__init__.py,sha256=huwWPmO3ACxg6vMvjul6Ty0HDdwwfk886M4-DU5Z_04,535
16
+ hex_zmq_servers/cam/realsense/cam_realsense.py,sha256=NIMjVDgLVFGGbNOcckfEukYoX1BThRxFqgg7mef38T8,5147
17
+ hex_zmq_servers/cam/realsense/cam_realsense_cli.py,sha256=qKx0PvsWtKbxYd-SOaEWFkpIHhPP8QDRzvTuUA9QKoc,814
18
+ hex_zmq_servers/cam/realsense/cam_realsense_srv.py,sha256=2NqXp96pu-LvBs91rNM-BJgbznD_COPcc8ZFeNxdGdw,2320
19
+ hex_zmq_servers/cam/rgb/__init__.py,sha256=cBrms_3YOQAp2ZtfvHkuaxqameSG1bALDgUcNrDpEQA,481
20
+ hex_zmq_servers/cam/rgb/cam_rgb.py,sha256=qYIfJS2bCckAXkn1lsw4XBvb4e2BbGtkhTVR6GvJZcA,4784
21
+ hex_zmq_servers/cam/rgb/cam_rgb_cli.py,sha256=fWshI38ShDCML6Mu9WmLI543as_kxBmP0nj8LBhM7FI,1084
22
+ hex_zmq_servers/cam/rgb/cam_rgb_srv.py,sha256=gsJBpcboRMGDZ1jviHDhFNhw8UWKzGsAY92dNzizW6Q,2266
15
23
  hex_zmq_servers/config/cam_berxel.json,sha256=G-eps3r7seBEMQEzxk1p1ObsBxmvageGbPdvtZ8gRe4,340
16
24
  hex_zmq_servers/config/cam_dummy.json,sha256=T6vCG9__Gos9M5seK7w43xbqttAI6P1iyjcB_W0AOu8,191
17
- hex_zmq_servers/config/mujoco_archer_y6.json,sha256=dEIrwUmQ8c7mT-qObb98kfgzuuNCO3OjSHusLBPzkhs,639
18
- hex_zmq_servers/config/mujoco_e3_desktop.json,sha256=dEIrwUmQ8c7mT-qObb98kfgzuuNCO3OjSHusLBPzkhs,639
25
+ hex_zmq_servers/config/cam_realsense.json,sha256=hEQw-fdzC0w49kgcaqf_VwLj_RKB27PGOupQ2f2ig_k,321
26
+ hex_zmq_servers/config/cam_rgb.json,sha256=EDPE9xYBRVqPiw5x_0F_zR1It9Q5Uo3HSV6PW9lsu28,491
27
+ hex_zmq_servers/config/mujoco_archer_y6.json,sha256=okinjE_oFpXeII9hDzoBJivTkwJ-8XLUq6XUms00lGc,669
28
+ hex_zmq_servers/config/mujoco_e3_desktop.json,sha256=IVvGgGOEbrU1cQmXuCdsgpvyyzKhTFAgVccG_8Lxnoc,735
19
29
  hex_zmq_servers/config/robot_dummy.json,sha256=mBKWmLz4S7K6fP0zYDImKPfnR6lerRjEv6Ore0jYQMQ,2963
20
30
  hex_zmq_servers/config/robot_gello.json,sha256=pXPAng92M_-dhnLbh1R_LXKeskSapSnoJhNS-u2dhvo,1157
21
- hex_zmq_servers/config/robot_hexarm.json,sha256=4D4D3auDl3h-Mw8U1jN8U1ZbnVUt5kBXpPSbBgxDM9M,689
31
+ hex_zmq_servers/config/robot_hexarm.json,sha256=VckqMsZ9aeYVxkNHWBPYIIMKsQHDc3gSYLptP5aFLzg,690
22
32
  hex_zmq_servers/config/zmq_dummy.json,sha256=T6vCG9__Gos9M5seK7w43xbqttAI6P1iyjcB_W0AOu8,191
23
33
  hex_zmq_servers/mujoco/__init__.py,sha256=OKG7aJ2BACpob_DuAvikLiRo4M_3DO9Ffapwen87k_0,870
24
- hex_zmq_servers/mujoco/mujoco_base.py,sha256=WHPbnrwfpfHMYBUiRH9sgrm21CnD2ERwerVkUwa47jQ,10283
34
+ hex_zmq_servers/mujoco/mujoco_base.py,sha256=e3QkZfxlE8MHPse1nKgTlCHvV-mNYfMMuZ7PtmyVVsA,11752
25
35
  hex_zmq_servers/mujoco/archer_y6/__init__.py,sha256=v1vApZ-qBLEcqFjDN0eff5kvkzO3s8QHBO4AitBODfc,559
26
- hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6.py,sha256=bH3Znu8twvu6fHMrYEpmqiPeC5HX1yGIR7afXzjU4uU,10521
27
- hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_cli.py,sha256=9FmNspbtepcJxAlQHOgJ66910EMx6wMfUTSWXn2nbT8,675
28
- hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_srv.py,sha256=djEJPKdMiiRwmFcsfZNMNq0x7GJFKjWCQU2QnoZcQmg,4905
29
- hex_zmq_servers/mujoco/archer_y6/model/robot.xml,sha256=fYBmuOGVVXJE7CLVzOZHUz-NaocjvcZdEtIkZ7Nbbv8,8163
30
- hex_zmq_servers/mujoco/archer_y6/model/scene.xml,sha256=X3BQtpHmZhjus-4iclolqKAQCeJjpPuG2KGnj0uOaBg,2122
36
+ hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6.py,sha256=viZI_bVbnWbhow86mKQ10Sb8PAfsriw5uNFhRgGwR4U,12073
37
+ hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_cli.py,sha256=xNn_tF1U49ECjYvhy3xq1KizXZ5qJyqCNdPtBEVJyTU,2074
38
+ hex_zmq_servers/mujoco/archer_y6/mujoco_archer_y6_srv.py,sha256=Pn51_bXLAa40cTMKpCvqU7aT1jpR_r9pMrW2subLHAs,4987
39
+ hex_zmq_servers/mujoco/archer_y6/model/robot.xml,sha256=U6Rrpfb0jhd8UMuZzY6YmmZVF6QFrU4vVJqQ7MoRsgI,8172
40
+ hex_zmq_servers/mujoco/archer_y6/model/scene.xml,sha256=00mk4AMmgE0vhHdxuZ6MkKqOBu0Nf17T7GudlpJNI1I,2122
31
41
  hex_zmq_servers/mujoco/archer_y6/model/setting.xml,sha256=4jhxau8xeYtnnMmvy6B79B8-w2HbK_PTGDHyIelDEQs,1997
32
42
  hex_zmq_servers/mujoco/archer_y6/model/assets/arm_base_link.STL,sha256=JUr1vVN2qyB05yAJHwEKXQ1zpZ4m7_Cw-LbVaC9_gEE,413584
33
43
  hex_zmq_servers/mujoco/archer_y6/model/assets/arm_link_1.STL,sha256=4tx8OgJT-yNsHLAUUU6G2sfnNlVy36G2OrLmy9jcJ_0,304784
@@ -46,11 +56,11 @@ hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_right_link_1.STL,sha256=fj
46
56
  hex_zmq_servers/mujoco/archer_y6/model/assets/gripper_right_link_2.STL,sha256=ucyn656VxBpRx3sshxLxMPJIUZfNfEQ90P-9b8sR0xg,80284
47
57
  hex_zmq_servers/mujoco/archer_y6/model/assets/table_link.STL,sha256=dqFXHRqjZSDyvYHCaJkbmYFsKngZRk1xjg_Zl2_jDc4,684
48
58
  hex_zmq_servers/mujoco/e3_desktop/__init__.py,sha256=pMLKpJGBHgc9IU142NcM7ubwdXq9E422k6Giui1A8i4,565
49
- hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop.py,sha256=dDlDkz9juUW-5AY3oH1aV9nOUoMu1PP9iAl6wRHUJE8,14607
50
- hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_cli.py,sha256=cyeelj4PL20cupPxvpoWne-b7hoe4-Cgmg7saFgMNdc,3385
51
- hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_srv.py,sha256=H26KCzZCaFpQcNG6MhUZTxkq1izQSegjVQVif85Q_Nw,8761
52
- hex_zmq_servers/mujoco/e3_desktop/model/robot.xml,sha256=vKBOND9TxBKv4bdyJPaXKNTeqfthXgOFQZ-N8yFfb3o,17012
53
- hex_zmq_servers/mujoco/e3_desktop/model/scene.xml,sha256=Qzeun0ohjx-nAcQ8mHXLmZMiBN-GbWjseRydVZu4PgE,2222
59
+ hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop.py,sha256=kLHb7HIevv7j4S-gwFJJ1G0XWN6eIa6rq8tXYHI7IzE,17918
60
+ hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_cli.py,sha256=AHmsX3cZx4iZX7q8itFVqvCGeiy_JQ4AsTzdyXLCR9E,7790
61
+ hex_zmq_servers/mujoco/e3_desktop/mujoco_e3_desktop_srv.py,sha256=dFGGmwHihrMDzAD1uptL91O9W1l7mKZRUlEWLkAZ7xU,8953
62
+ hex_zmq_servers/mujoco/e3_desktop/model/robot.xml,sha256=hLp385sG037rw2bAPu6tKoa5XYQLwWb4YawijYr7Ebs,17030
63
+ hex_zmq_servers/mujoco/e3_desktop/model/scene.xml,sha256=8zs5vCf_RvB1wUlD9ymVMpxyU2Eq0XzqEQnXm_wHhdA,2222
54
64
  hex_zmq_servers/mujoco/e3_desktop/model/setting.xml,sha256=Y-GIwzWi46SJsBQZs0g0G7MDuzQmCGbHaVmdPZGYL14,4472
55
65
  hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_base_link.STL,sha256=JUr1vVN2qyB05yAJHwEKXQ1zpZ4m7_Cw-LbVaC9_gEE,413584
56
66
  hex_zmq_servers/mujoco/e3_desktop/model/assets/arm_link_1.STL,sha256=4tx8OgJT-yNsHLAUUU6G2sfnNlVy36G2OrLmy9jcJ_0,304784
@@ -70,18 +80,18 @@ hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_right_link_1.STL,sha256=f
70
80
  hex_zmq_servers/mujoco/e3_desktop/model/assets/gripper_right_link_2.STL,sha256=ucyn656VxBpRx3sshxLxMPJIUZfNfEQ90P-9b8sR0xg,80284
71
81
  hex_zmq_servers/mujoco/e3_desktop/model/assets/table_link.STL,sha256=dqFXHRqjZSDyvYHCaJkbmYFsKngZRk1xjg_Zl2_jDc4,684
72
82
  hex_zmq_servers/robot/__init__.py,sha256=PqzGhsdbaeyYa7EZpAYMvT9myYhCO3OTbSPXVp1uiUM,1026
73
- hex_zmq_servers/robot/robot_base.py,sha256=od6KXOcRW7OlT0DBR7MJtBWD986giqYeeitnQz0uBG8,7119
83
+ hex_zmq_servers/robot/robot_base.py,sha256=-or25jU3x7kJaIx2c7l83nPLqb93yL4IHIWjfDGkivo,8064
74
84
  hex_zmq_servers/robot/dummy/__init__.py,sha256=_lDtPH-JQ1ltPB8JzD7wLu77iwNOgEZ3fJkz8MNKXrw,517
75
- hex_zmq_servers/robot/dummy/robot_dummy.py,sha256=6bRgpKmExjTq_glDdNyELzRYcLTXgfbeRBMxCEcCpEY,2565
76
- hex_zmq_servers/robot/dummy/robot_dummy_cli.py,sha256=ZZ1a0ABE173oZ1elLY-gINpMUxI7vM-zfMO9g1M0nt0,667
85
+ hex_zmq_servers/robot/dummy/robot_dummy.py,sha256=vZTrEvIFsW5ne123hEGoynLGnQH2T-jxLBVww6LEwfM,2665
86
+ hex_zmq_servers/robot/dummy/robot_dummy_cli.py,sha256=kjugt5By71Nj0wFb2tnQgRdSyYYZeUomwh96UCCR-No,699
77
87
  hex_zmq_servers/robot/dummy/robot_dummy_srv.py,sha256=E0a0arRgAQiCfEXnrJ1l6TP8Cijy497WfX--padg7B8,2576
78
88
  hex_zmq_servers/robot/gello/__init__.py,sha256=7xSTc8ZB-bQODyEo6pzPz3ILZ1eAgVSsy6BmF2ECKeQ,517
79
- hex_zmq_servers/robot/gello/robot_gello.py,sha256=S2CZ5N9tRXP_vvlR_RPSUMLzxkhhYl1Ag9HpiltEOAc,13000
80
- hex_zmq_servers/robot/gello/robot_gello_cli.py,sha256=xQuSrkzwAr4tuXW1P-rRtSXH3A8RRX2eJmIvihAgEkI,667
89
+ hex_zmq_servers/robot/gello/robot_gello.py,sha256=DyMELg490HykRYdbs4QVluR8oC7KAYWAju0OEmaowPE,13100
90
+ hex_zmq_servers/robot/gello/robot_gello_cli.py,sha256=wnbRVrpIEE8G4ZzfjuSJA5TMPig-2I1oLJE-6b0kFSY,699
81
91
  hex_zmq_servers/robot/gello/robot_gello_srv.py,sha256=vCWgKpsO4MhZLspdaG45uHj8EB0gnptuj2SuxCaoEVI,2841
82
92
  hex_zmq_servers/robot/hexarm/__init__.py,sha256=07EUc2fCKqxYZbUUcDeD_AXvCAWI6r1qBjpWJ58_AcY,1414
83
- hex_zmq_servers/robot/hexarm/robot_hexarm.py,sha256=TdUxRjkNfqOpPYWbdiCARhrpPRSpBfIzXXUC5HMVsn0,8991
84
- hex_zmq_servers/robot/hexarm/robot_hexarm_cli.py,sha256=0L5RDIWn16qtZMKfIg2VkSYF46H3FJEyZLiUF6sm3KY,820
93
+ hex_zmq_servers/robot/hexarm/robot_hexarm.py,sha256=dUd_6JSHvlEIxkYLF1xCBZTOOcxt_VcfS2qcb-vEkgU,10364
94
+ hex_zmq_servers/robot/hexarm/robot_hexarm_cli.py,sha256=KO-I_CCjWwdckRKlxX7gIxUgsUKW3rcOycBo1nAxFEY,852
85
95
  hex_zmq_servers/robot/hexarm/robot_hexarm_srv.py,sha256=PE_WyLRXV_DbYVVQ0Wd60KlZZ37vfoEJVEhf2_4xADQ,2759
86
96
  hex_zmq_servers/robot/hexarm/urdf/archer_l6y/empty.urdf,sha256=g7Yn_uIM_ddb_K32FnDgHWSsvxH08oaPDiMb0yUb0Lg,7808
87
97
  hex_zmq_servers/robot/hexarm/urdf/archer_l6y/gp100.urdf,sha256=YJ_S1Hfwg_PV1nr5QZJuikuCO18GqNu6RDA3gEwG-Pk,7620
@@ -93,8 +103,8 @@ hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100.urdf,sha256=i8vpARxlQpYskLmTZ7
93
103
  hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_handle.urdf,sha256=Tpii1Wk26jRtMqLWI2knrqBTIh7hCjpgGySlrBgdvpA,7458
94
104
  hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_p050.urdf,sha256=6Zzc_PXDwtxw9xQEXQOH0hu_moaiQzeMC22hUqAQS9s,7459
95
105
  hex_zmq_servers/robot/hexarm/urdf/archer_y6/gp100_p050_handle.urdf,sha256=HNa7wweNYUj8YtSUfx87ASeNEIPIjB-gZ4O7RyHng6g,7456
96
- hex_zmq_servers-0.3.2.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
97
- hex_zmq_servers-0.3.2.dist-info/METADATA,sha256=PmrtXTqSwahoQoeRRvEZw5WXzEVE3IiFXlldoIlenPg,4726
98
- hex_zmq_servers-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
99
- hex_zmq_servers-0.3.2.dist-info/top_level.txt,sha256=lPH1DfgMrQOe0Grh8zSZopf6LmnLvb_aStVmZ41PyAg,16
100
- hex_zmq_servers-0.3.2.dist-info/RECORD,,
106
+ hex_zmq_servers-0.3.4.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
107
+ hex_zmq_servers-0.3.4.dist-info/METADATA,sha256=vlE6l40uTHq955ndiXzA52hwcjhW7CvLfbPxwVx7Y6c,4750
108
+ hex_zmq_servers-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
+ hex_zmq_servers-0.3.4.dist-info/top_level.txt,sha256=lPH1DfgMrQOe0Grh8zSZopf6LmnLvb_aStVmZ41PyAg,16
110
+ hex_zmq_servers-0.3.4.dist-info/RECORD,,