dexcontrol 0.2.12__py3-none-any.whl → 0.3.0__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.

Potentially problematic release.


This version of dexcontrol might be problematic. Click here for more details.

@@ -12,8 +12,11 @@
12
12
 
13
13
  import os
14
14
  import re
15
- from typing import Final
15
+ from typing import Any, Final
16
16
 
17
+ from loguru import logger
18
+
19
+ import dexcontrol
17
20
  from dexcontrol.utils.constants import ROBOT_NAME_ENV_VAR
18
21
 
19
22
 
@@ -56,3 +59,182 @@ def get_robot_model() -> str:
56
59
  raise ValueError(f"Unknown robot model: {robot_model_abb}")
57
60
  model = robot_model_abb_mapping[robot_model_abb] + "-" + robot_name.split("-")[-1]
58
61
  return model
62
+
63
+
64
+ def check_version_compatibility(
65
+ version_info: dict[str, Any], show_warnings: bool = True
66
+ ) -> None:
67
+ """Check version compatibility between client and server.
68
+
69
+ This function uses the new JSON-based version interface to:
70
+ 1. Compare client library version with server's minimum required version
71
+ 2. Check server component versions for compatibility
72
+ 3. Provide clear guidance for version mismatches
73
+
74
+ Args:
75
+ version_info: Dictionary containing version information from get_version_info()
76
+ show_warnings: Whether to show warning messages (default: True)
77
+ """
78
+ validate_client_version(version_info)
79
+ validate_server_version(version_info)
80
+
81
+
82
+ def validate_client_version(version_info: dict[str, Any]) -> None:
83
+ """Validate client library version against server requirements.
84
+
85
+ Args:
86
+ version_info: Dictionary containing server and client version information.
87
+ """
88
+
89
+ client_info = version_info.get("client", {})
90
+ min_required_version = client_info.get("minimal_version")
91
+
92
+ if not min_required_version:
93
+ logger.debug("No minimum version requirement from server")
94
+ return
95
+
96
+ # Get current library version
97
+ current_version = getattr(dexcontrol, "__version__", "unknown")
98
+
99
+ if current_version == "unknown":
100
+ logger.warning("Could not determine current library version")
101
+ return
102
+
103
+ # Compare versions
104
+ comparison = compare_versions(current_version, min_required_version)
105
+
106
+ if comparison < 0:
107
+ show_version_upgrade_warning(current_version, min_required_version)
108
+
109
+
110
+ def show_version_upgrade_warning(current: str, required: str) -> None:
111
+ """Display version upgrade warning to user.
112
+
113
+ Args:
114
+ current: Current library version
115
+ required: Required minimum version
116
+ """
117
+ logger.error(
118
+ f"🚨 CLIENT VERSION TOO OLD! 🚨\n"
119
+ f"Current library version: {current}\n"
120
+ f"Minimum required version: {required}\n"
121
+ f"\n"
122
+ f"⚠️ Your dexcontrol library is outdated and may not work correctly!\n"
123
+ f"📦 Please update your library using: pip install --upgrade dexcontrol\n"
124
+ )
125
+
126
+
127
+ def validate_server_version(version_info: dict[str, Any]) -> None:
128
+ """Validate server software versions against minimum requirements.
129
+
130
+ Args:
131
+ version_info: Dictionary containing server and client version information.
132
+ """
133
+ server_info = version_info.get("server", {})
134
+
135
+ if not server_info:
136
+ logger.debug("No server version information available")
137
+ return
138
+
139
+ # Check each component's software version
140
+ components_below_min = []
141
+ for component_name, component_info in server_info.items():
142
+ if isinstance(component_info, dict):
143
+ software_version = component_info.get("software_version")
144
+ if software_version is not None:
145
+ try:
146
+ # Convert to int if it's a string
147
+ software_version_int = int(software_version)
148
+ if software_version_int < dexcontrol.MIN_SOC_SOFTWARE_VERSION:
149
+ components_below_min.append(
150
+ (component_name, software_version_int)
151
+ )
152
+ except (ValueError, TypeError) as e:
153
+ logger.debug(
154
+ f"Could not parse software version for {component_name}: {e}"
155
+ )
156
+
157
+ # If any components are below minimum version, show warning
158
+ if components_below_min:
159
+ show_server_version_warning(
160
+ components_below_min, dexcontrol.MIN_SOC_SOFTWARE_VERSION
161
+ )
162
+
163
+
164
+ def show_server_version_warning(
165
+ components: list[tuple[str, int]], min_version: int
166
+ ) -> None:
167
+ """Display server version warning to user.
168
+
169
+ Args:
170
+ components: List of (component_name, version) tuples for components below minimum.
171
+ min_version: Minimum required server software version.
172
+ """
173
+ components_str = "\n".join(
174
+ f" - {name}: version {version}" for name, version in components
175
+ )
176
+
177
+ logger.error(
178
+ f"🚨 SERVER VERSION TOO OLD! 🚨\n"
179
+ f"The following server components are below minimum version {min_version}:\n"
180
+ f"{components_str}\n"
181
+ f"\n"
182
+ f"⚠️ Your robot's firmware may be outdated and some features may not work correctly!\n"
183
+ f"📦 Please contact your robot admin or check https://github.com/dexmate-ai/vega-firmware.\n"
184
+ )
185
+
186
+
187
+ def compare_versions(version1: str, version2: str) -> int:
188
+ """Compare two semantic version strings.
189
+
190
+ Args:
191
+ version1: First version string (e.g., "1.2.3")
192
+ version2: Second version string (e.g., "1.1.0")
193
+
194
+ Returns:
195
+ -1 if version1 < version2
196
+ 0 if version1 == version2
197
+ 1 if version1 > version2
198
+ """
199
+ try:
200
+ # Clean version strings (remove 'v' prefix, handle pre-release suffixes)
201
+ def clean_version(v: str) -> list[int]:
202
+ v = v.strip().lower()
203
+ if v.startswith("v"):
204
+ v = v[1:]
205
+ # Split by dots and take only numeric parts
206
+ parts = v.split(".")
207
+ numeric_parts = []
208
+ for part in parts:
209
+ # Remove any non-numeric suffixes (like -alpha, -rc1, etc.)
210
+ numeric_part = ""
211
+ for char in part:
212
+ if char.isdigit():
213
+ numeric_part += char
214
+ else:
215
+ break
216
+ if numeric_part:
217
+ numeric_parts.append(int(numeric_part))
218
+ return numeric_parts
219
+
220
+ parts1 = clean_version(version1)
221
+ parts2 = clean_version(version2)
222
+
223
+ # Pad shorter version with zeros
224
+ max_len = max(len(parts1), len(parts2))
225
+ parts1.extend([0] * (max_len - len(parts1)))
226
+ parts2.extend([0] * (max_len - len(parts2)))
227
+
228
+ # Compare part by part
229
+ for p1, p2 in zip(parts1, parts2):
230
+ if p1 < p2:
231
+ return -1
232
+ elif p1 > p2:
233
+ return 1
234
+
235
+ return 0
236
+
237
+ except Exception as e:
238
+ logger.debug(f"Version comparison error: {e}")
239
+ # Fallback to string comparison
240
+ return -1 if version1 < version2 else (1 if version1 > version2 else 0)
@@ -20,28 +20,6 @@ TYPE_SOFTWARE_VERSION = dict[
20
20
  ]
21
21
 
22
22
 
23
- def software_version_to_dict(
24
- version_msg: dexcontrol_query_pb2.SoftwareVersion,
25
- ) -> dict[str, TYPE_SOFTWARE_VERSION]:
26
- """Convert a SoftwareVersion protobuf message to a dictionary.
27
-
28
- Args:
29
- version_msg: SoftwareVersion protobuf message.
30
-
31
- Returns:
32
- Dictionary containing version information with component names as keys.
33
- """
34
- return {
35
- key: {
36
- "hardware_version": value.hardware_version,
37
- "software_version": value.software_version,
38
- "main_hash": value.main_hash,
39
- "compile_time": value.compile_time,
40
- }
41
- for key, value in version_msg.firmware_version.items()
42
- }
43
-
44
-
45
23
  class ComponentStatus(Enum):
46
24
  """Enum representing the status of a component."""
47
25
 
@@ -10,20 +10,158 @@
10
10
 
11
11
  """Zenoh utilities for dexcontrol.
12
12
 
13
- This module provides general utility functions for working with Zenoh
14
- communication framework.
13
+ This module provides comprehensive utility functions for working with Zenoh
14
+ communication framework, including session management, configuration loading,
15
+ JSON queries, and statistics computation.
15
16
  """
16
17
 
18
+ import gc
17
19
  import json
20
+ import threading
18
21
  import time
22
+ from pathlib import Path
23
+ from typing import TYPE_CHECKING
19
24
 
20
25
  import numpy as np
21
26
  import zenoh
22
27
  from loguru import logger
28
+ from omegaconf import DictConfig, OmegaConf
23
29
 
30
+ import dexcontrol
31
+ from dexcontrol.config.vega import get_vega_config
24
32
  from dexcontrol.utils.os_utils import resolve_key_name
25
33
 
34
+ if TYPE_CHECKING:
35
+ from dexcontrol.config.vega import VegaConfig
26
36
 
37
+
38
+ # =============================================================================
39
+ # Session Management Functions
40
+ # =============================================================================
41
+
42
+
43
+ def get_default_zenoh_config() -> str | None:
44
+ """Gets the default zenoh configuration file path.
45
+
46
+ Returns:
47
+ Path to default config file if it exists, None otherwise.
48
+ """
49
+ default_path = dexcontrol.COMM_CFG_PATH
50
+ if not default_path.exists():
51
+ logger.warning(f"Zenoh config file not found at {default_path}")
52
+ logger.warning("Please use dextop to set up the zenoh config file")
53
+ return None
54
+ return str(default_path)
55
+
56
+
57
+ def create_zenoh_session(zenoh_config_file: str | None = None) -> zenoh.Session:
58
+ """Creates and initializes a Zenoh communication session.
59
+
60
+ Args:
61
+ zenoh_config_file: Path to zenoh configuration file. If None,
62
+ uses the default configuration path.
63
+
64
+ Returns:
65
+ Initialized zenoh session.
66
+
67
+ Raises:
68
+ RuntimeError: If zenoh session initialization fails.
69
+ """
70
+ try:
71
+ config_path = zenoh_config_file or get_default_zenoh_config()
72
+ if config_path is None:
73
+ logger.warning("Using default zenoh config settings")
74
+ return zenoh.open(zenoh.Config())
75
+ return zenoh.open(zenoh.Config.from_file(config_path))
76
+ except Exception as e:
77
+ raise RuntimeError(f"Failed to initialize zenoh session: {e}") from e
78
+
79
+
80
+ def load_robot_config(
81
+ robot_config_path: str | None = None,
82
+ ) -> "VegaConfig":
83
+ """Load robot configuration from file or use default variant.
84
+
85
+ Args:
86
+ robot_config_path: Path to robot configuration file. If None,
87
+ uses default configuration for detected robot model.
88
+
89
+ Returns:
90
+ Robot configuration as OmegaConf object.
91
+
92
+ Raises:
93
+ ValueError: If configuration cannot be loaded or parsed.
94
+ """
95
+ try:
96
+ if robot_config_path is not None:
97
+ # Load custom configuration from file
98
+ config_path = Path(robot_config_path)
99
+ if not config_path.exists():
100
+ raise ValueError(f"Configuration file not found: {config_path}")
101
+
102
+ # Load YAML configuration and merge with default
103
+ base_config = DictConfig, get_vega_config()
104
+ custom_config = OmegaConf.load(config_path)
105
+ return OmegaConf.merge(base_config, custom_config)
106
+ else:
107
+ # Use default configuration for detected robot model
108
+ try:
109
+ return get_vega_config()
110
+ except ValueError as e:
111
+ # If robot model detection fails, use default vega-1 config
112
+ if "Robot name is not set" in str(e):
113
+ logger.warning(
114
+ "Robot model not detected, using default vega-1 configuration"
115
+ )
116
+ return get_vega_config("vega-1")
117
+ raise
118
+
119
+ except Exception as e:
120
+ raise ValueError(f"Failed to load robot configuration: {e}") from e
121
+
122
+
123
+ def create_standalone_robot_interface(
124
+ zenoh_config_file: str | None = None,
125
+ robot_config_path: str | None = None,
126
+ ) -> tuple[zenoh.Session, "VegaConfig"]:
127
+ """Create standalone zenoh session and robot configuration.
128
+
129
+ This function provides a convenient way to create both a zenoh session
130
+ and robot configuration for use with RobotQueryInterface without
131
+ requiring the full Robot class initialization.
132
+
133
+ Args:
134
+ zenoh_config_file: Path to zenoh configuration file. If None,
135
+ uses the default configuration path.
136
+ robot_config_path: Path to robot configuration file. If None,
137
+ uses default configuration for detected robot model.
138
+
139
+ Returns:
140
+ Tuple of (zenoh_session, robot_config) ready for use with
141
+ RobotQueryInterface.
142
+
143
+ Raises:
144
+ RuntimeError: If zenoh session initialization fails.
145
+ ValueError: If robot configuration cannot be loaded.
146
+
147
+ Example:
148
+ >>> session, config = create_standalone_robot_interface()
149
+ >>> query_interface = RobotQueryInterface(session, config)
150
+ >>> version_info = query_interface.get_version_info()
151
+ >>> session.close()
152
+ """
153
+ # Create zenoh session
154
+ session = create_zenoh_session(zenoh_config_file)
155
+
156
+ # Load robot configuration
157
+ config = load_robot_config(robot_config_path)
158
+
159
+ return session, config
160
+
161
+
162
+ # =============================================================================
163
+ # Query and Communication Functions
164
+ # =============================================================================
27
165
  def query_zenoh_json(
28
166
  zenoh_session: zenoh.Session,
29
167
  topic: str,
@@ -84,6 +222,115 @@ def query_zenoh_json(
84
222
  return None
85
223
 
86
224
 
225
+ # =============================================================================
226
+ # Cleanup and Exit Handling Functions
227
+ # =============================================================================
228
+ def close_zenoh_session_with_timeout(
229
+ session: zenoh.Session, timeout: float = 2.0
230
+ ) -> tuple[bool, Exception | None]:
231
+ """Close a Zenoh session with timeout handling.
232
+
233
+ This function attempts to close a Zenoh session gracefully with a timeout.
234
+ If the close operation takes too long, it returns with a timeout indication.
235
+
236
+ Args:
237
+ session: The Zenoh session to close.
238
+ timeout: Maximum time to wait for session close (default 2.0 seconds).
239
+
240
+ Returns:
241
+ Tuple of (success, exception):
242
+ - success: True if session closed successfully, False otherwise
243
+ - exception: Any exception that occurred during close, or None
244
+ """
245
+
246
+ close_success = False
247
+ close_exception = None
248
+
249
+ def _close_session():
250
+ """Inner function to close the session."""
251
+ nonlocal close_success, close_exception
252
+ try:
253
+ session.close()
254
+ close_success = True
255
+ except Exception as e: # pylint: disable=broad-except
256
+ close_exception = e
257
+ logger.debug(f"Zenoh session close attempt failed: {e}")
258
+ # Try to trigger garbage collection as fallback
259
+ try:
260
+ gc.collect()
261
+ except Exception: # pylint: disable=broad-except
262
+ pass
263
+
264
+ # Try to close zenoh session with timeout
265
+ close_thread = threading.Thread(target=_close_session, daemon=True)
266
+ close_thread.start()
267
+
268
+ # Use progressive timeout strategy
269
+ timeouts = [timeout / 2, timeout / 2] # Split timeout into two attempts
270
+ for i, wait_time in enumerate(timeouts):
271
+ close_thread.join(timeout=wait_time)
272
+ if not close_thread.is_alive():
273
+ break
274
+
275
+ if close_thread.is_alive():
276
+ return False, Exception("Close operation timed out")
277
+ elif close_success:
278
+ return True, None
279
+ else:
280
+ logger.debug(f"Zenoh session closed with error: {close_exception}")
281
+ return False, close_exception
282
+
283
+
284
+ def wait_for_zenoh_cleanup(cleanup_delays: list[float] | None = None) -> list[str]:
285
+ """Wait for Zenoh internal threads to clean up.
286
+
287
+ This function waits for Zenoh's internal pyo3 threads to clean up after
288
+ session closure, using progressive delays to balance responsiveness and
289
+ thoroughness.
290
+
291
+ Args:
292
+ cleanup_delays: List of delays in seconds to wait between checks.
293
+ Defaults to [0.1, 0.2, 0.3] if not provided.
294
+
295
+ Returns:
296
+ List of thread names that are still active after cleanup attempts.
297
+ """
298
+ if cleanup_delays is None:
299
+ cleanup_delays = [0.1, 0.2, 0.3] # Progressive delays totaling 0.6s
300
+
301
+ for delay in cleanup_delays:
302
+ time.sleep(delay)
303
+ # Check if threads are still active
304
+ active_threads = get_active_zenoh_threads()
305
+ if not active_threads:
306
+ return []
307
+
308
+ # Return any remaining threads
309
+ lingering_threads = get_active_zenoh_threads()
310
+ if lingering_threads:
311
+ logger.debug(
312
+ f"Note: {len(lingering_threads)} Zenoh internal thread(s) still active. "
313
+ "These typically clean up after script exit."
314
+ )
315
+ return lingering_threads
316
+
317
+
318
+ def get_active_zenoh_threads() -> list[str]:
319
+ """Get list of active Zenoh (pyo3) threads.
320
+
321
+ Returns:
322
+ List of thread names that are pyo3-related and still active.
323
+ """
324
+ return [
325
+ t.name
326
+ for t in threading.enumerate()
327
+ if "pyo3" in t.name and t.is_alive() and not t.daemon
328
+ ]
329
+
330
+
331
+ # =============================================================================
332
+ # Statistics and Analysis Functions
333
+ # =============================================================================
87
334
  def compute_ntp_stats(offsets: list[float], rtts: list[float]) -> dict[str, float]:
88
335
  """Compute NTP statistics, removing outliers based on RTT median and std.
89
336
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dexcontrol
3
- Version: 0.2.12
3
+ Version: 0.3.0
4
4
  Summary: A Python library of Sensing and Control for Dexmate's Robot
5
5
  Project-URL: Repository, https://github.com/dexmate-ai/dexcontrol
6
6
  Author-email: Dexmate <contact@dexmate.ai>
@@ -253,6 +253,17 @@ To run the examples in this repo, you can try:
253
253
  pip install dexcontrol[example]
254
254
  ```
255
255
 
256
+ ## ⚠️ Version Compatibility
257
+
258
+ **Important:** `dexcontrol >= 0.3.0` requires robot firmware with SoC version `286` or higher.
259
+
260
+ **Before upgrading, check your current firmware version:**
261
+ ```shell
262
+ dextop firmware info
263
+ ```
264
+
265
+ If your firmware is outdated, please update it before installing the new version to ensure full compatibility. Please contact the Dexmate team if you do not know how to do it.
266
+
256
267
  ## 📄 Licensing
257
268
 
258
269
  This project is **dual-licensed**:
@@ -1,12 +1,12 @@
1
- dexcontrol/__init__.py,sha256=9y7PGcWv9KYcLg7nH54FQv2vv0Q_GeVy3wcFa4Yf_gs,1692
2
- dexcontrol/robot.py,sha256=Ab-9HhfPiqKq4gGNoBUUMKqYsjPK4u9KW2ykPlbKe4A,51914
1
+ dexcontrol/__init__.py,sha256=Cr2LZw5hPGiwC-Uz7VYgVdLN6AsEmCwTRu_eyKRXrec,1746
2
+ dexcontrol/robot.py,sha256=zvUVgc9hfYrxxD9qsUzEtdAVc59RdE74-yBwUjoy2xw,41385
3
3
  dexcontrol/apps/dualsense_teleop_base.py,sha256=Dw1z-2HA5D7DPKutZxlOdXsN9vpk4gS6XzJsL5ZQLM0,12702
4
4
  dexcontrol/config/__init__.py,sha256=UVLNpzGD14e8g68rUZFXTh0B7FRx6uS0Eg_MecjinYM,520
5
- dexcontrol/config/vega.py,sha256=UE5XAdk5IpYj-LgLUzbAvr6Zi83e4XYPAvXdgM9z3VY,8239
5
+ dexcontrol/config/vega.py,sha256=Uh14vBOZoMAmFXQgjh-IK9x_W0j8YvqgUsoDNHx1ZsE,8443
6
6
  dexcontrol/config/core/__init__.py,sha256=Ym2R1hr1iMKQuXcg16BpZfQtTb0hQ5Q7smUIMlwKfho,637
7
7
  dexcontrol/config/core/arm.py,sha256=5hN1dQMe2H6oufaqgtZqx9vuB969DxM26leJqPsKEiA,1471
8
- dexcontrol/config/core/chassis.py,sha256=163hO4lVVaW7r9dvNleH0cdDds_GfO15EepnjloeT9U,868
9
- dexcontrol/config/core/hand.py,sha256=bGRO-_usaFP0GU6jgB6Lm3Z07DMqMVO_tiB5j4c8Cu4,954
8
+ dexcontrol/config/core/chassis.py,sha256=2FjyFujg2q7aw8J9BklNM7eeLxscY9BSTlBvz-tLwOM,1092
9
+ dexcontrol/config/core/hand.py,sha256=r6XVyGCuwv7MFmaMLn7l3iPZUH376NZSmtsfLnznAgw,1033
10
10
  dexcontrol/config/core/head.py,sha256=SLwZE-lYEOk8XAmW-Ex7VkLF2w5HLItwsA3Dc7n5FtE,1061
11
11
  dexcontrol/config/core/misc.py,sha256=zHkJ144b6kbmMFE63wy_fgfo_6V-4XmM19hr6BUtQ0Y,1567
12
12
  dexcontrol/config/core/torso.py,sha256=DCTFgN1_Gn4THkKy23sIHOedACQtQ7cET3g4AmkVPco,1460
@@ -24,17 +24,18 @@ dexcontrol/config/sensors/lidar/rplidar.py,sha256=ybuT_f1ADWF3oGH1gi6D2F80TbJEm4
24
24
  dexcontrol/config/sensors/ultrasonic/__init__.py,sha256=-q83RhIMZJGVFVPYaA4hOugoG6wZw8EL6wJg7-HTSxU,294
25
25
  dexcontrol/config/sensors/ultrasonic/ultrasonic.py,sha256=7b4dm1QOhy5_5RFVpY-frXZyDzqok0K1u7ed9gf3PL0,552
26
26
  dexcontrol/core/__init__.py,sha256=bYPMLxbbn5QeuPyA6OPGDS2JTYpnVvaZJT8PeILFjQY,252
27
- dexcontrol/core/arm.py,sha256=wbRm7G4v3ByVwscZ9rEu_IpmBdqZoI6gKXDphDeNN5Q,15360
28
- dexcontrol/core/chassis.py,sha256=tB-tsx6MjKJDOtQxBf7PIX0L1JwgnsXUzmNPC1VKsQU,23165
29
- dexcontrol/core/component.py,sha256=mkYH7W7tWIWnfWfXmCUQulzaZ7VfrwYI0ET_L7pPw2k,34779
30
- dexcontrol/core/hand.py,sha256=wd__YB5wcM-LlksEm3PatXhy665zLnu9nV_9x85WxIA,8649
31
- dexcontrol/core/head.py,sha256=iz9umFxiizYzK49BdML1i6pKM_i8kbk44tH0uu_TaVM,10512
32
- dexcontrol/core/misc.py,sha256=0YNJCrTJS1yCT9N-p9us5I7uSiAK470k8nWgJu638X4,25153
33
- dexcontrol/core/torso.py,sha256=4MB5EojHnlBpUaHzcZOh4IkKcjI6pSluXmpb5RpmdV4,8932
34
- dexcontrol/proto/dexcontrol_msg_pb2.py,sha256=YgPlHnOZbVfCuTdfK1DD0t4yf6qeDf-ximl4qY5YeWo,6397
35
- dexcontrol/proto/dexcontrol_msg_pb2.pyi,sha256=5sI3nH1hhLPAg_UpNk5Mgh5bt5YrzcoPj1tCL6bT45s,10865
36
- dexcontrol/proto/dexcontrol_query_pb2.py,sha256=n5EazrveRw2UXFM-J5n5c7NVfr8LzaPigmOns4UOI9k,5946
37
- dexcontrol/proto/dexcontrol_query_pb2.pyi,sha256=0Lr0wYPGHhhNeAK24JJ2A7bzZ7q-r5ssOI2_ZfoYzVk,7108
27
+ dexcontrol/core/arm.py,sha256=7kBm5rTcw0K_GmIo22tJ1m03KS5ObYYp_qYjWXLI-z4,16132
28
+ dexcontrol/core/chassis.py,sha256=9XSxu9dpLp027nlXVoU6C_My92f6RRsgKZIh1WwP2Y4,24693
29
+ dexcontrol/core/component.py,sha256=i1T1AOUtzCWOymNkkIUaVAAqVu4CWrsZmaRUoI1V2BI,35713
30
+ dexcontrol/core/hand.py,sha256=iu6aY5bZRi7reXaHpf2EyVaiAi5pUVxCojq-EOUwJiw,9644
31
+ dexcontrol/core/head.py,sha256=NfulFdgCfLBoNn40fb3-Gw7y0OmSAEF4xVWC_3qYHfk,10543
32
+ dexcontrol/core/misc.py,sha256=x7-Fxj_drCGP6deqWZG7MvuXWeRz_f59qSFg1lZJEaU,30406
33
+ dexcontrol/core/robot_query_interface.py,sha256=c395oggoKsfAYAYOQipKBwW5gBCwFXRxqNYdwHY4ZSs,17113
34
+ dexcontrol/core/torso.py,sha256=UlY1T8ENiHoiW9OWGRHfP6viOChg6kIuTNg9aNCioI4,8937
35
+ dexcontrol/proto/dexcontrol_msg_pb2.py,sha256=pIbSlweIVSU3hN13kZxPycySU_OYXPoHRuDh7NIBXho,5289
36
+ dexcontrol/proto/dexcontrol_msg_pb2.pyi,sha256=1KpTJnLq6KG2eijZ_vlFq1JsTPeCeLz1IdmoUhW6Tec,8722
37
+ dexcontrol/proto/dexcontrol_query_pb2.py,sha256=KXPCAaKb2n55MBxmhIaFvAoqbS9XsKD6RdztGNe0occ,6230
38
+ dexcontrol/proto/dexcontrol_query_pb2.pyi,sha256=CGwwceF5Ibt9U3R8x2e7GjVNv2GyuD5WM_nyN4YF2wQ,7822
38
39
  dexcontrol/sensors/__init__.py,sha256=3DtVBwX5E3RPPjfCh2elnqk6uIWB_ukIqPQgBPZ7w7g,1008
39
40
  dexcontrol/sensors/manager.py,sha256=Qsbs9zZ4TvJgyn8yuI85wOU6mqgBQEus_MYSXj7wA9w,7025
40
41
  dexcontrol/sensors/ultrasonic.py,sha256=WAHvHh64iQ0HfqVf-Oo0Rg8R32Cdk5d8k8kDSwa3Xrc,3258
@@ -52,14 +53,14 @@ dexcontrol/utils/constants.py,sha256=6SG5HoSo7V-DlWH7cdNaMJtZs05dWrqYWIuKpmXfdI0
52
53
  dexcontrol/utils/error_code.py,sha256=iy840qnWn9wv_nVqyEDP8-l2CuXlPH2xXXW0k-5cHKk,7321
53
54
  dexcontrol/utils/io_utils.py,sha256=4TYV33ufECo8fuQivrZR9vtSdwWYUiPvpAUSneEzOOs,850
54
55
  dexcontrol/utils/motion_utils.py,sha256=p4kXQm_YorISDC2crrpY0gwCVw_yQCPv-acxPUSfh8w,7172
55
- dexcontrol/utils/os_utils.py,sha256=CC2st_Pb0C5cWfCg-i1P5bgBx3ZBX4LWVUiBq63dNeo,1852
56
- dexcontrol/utils/pb_utils.py,sha256=_6nFLKwAtq56vYXMZ6YmHnMfL5SarD96MVEA-9cxXns,3178
56
+ dexcontrol/utils/os_utils.py,sha256=63sMR6fbd1GgAn6GmmYbSvPfA5dNPRJ-8Yv9VwotRuI,8185
57
+ dexcontrol/utils/pb_utils.py,sha256=zN4pMS9GV9OTj1TmhcWTaDmfmgttyIDFJEuOE5tbCS0,2508
57
58
  dexcontrol/utils/rate_limiter.py,sha256=wFNaJ1fh-GO6zItuksKd_DSxLA1esE71WAiNDLpGsU0,6176
58
59
  dexcontrol/utils/rtc_utils.py,sha256=o2F9puC7CdAPnqiVq2vzomFZ7hMHljwtAbp9UiLhxJY,4426
59
60
  dexcontrol/utils/timer.py,sha256=1sOYYEapbZ5aBqJwknClsxgjDx0FDRQuGEdcTGnYTCI,3948
60
61
  dexcontrol/utils/trajectory_utils.py,sha256=TURFb0DeDey0416z4L7AXiWcKJYsgg_bB5AE_JPSpXY,1879
61
62
  dexcontrol/utils/viz_utils.py,sha256=rKtZfu32-9D9CS4cSiil-oLub_MiKTJV6hURvJbKd0s,6295
62
- dexcontrol/utils/zenoh_utils.py,sha256=83BSwWM0RZQP5Zey4Py83P_QC-vuk1qyk2Ac5iuschA,4264
63
+ dexcontrol/utils/zenoh_utils.py,sha256=JiwEmCHEDk-EWtoUjIGw-VgYu1mapUSR1S-OcD2R7Ro,13166
63
64
  dexcontrol/utils/subscribers/__init__.py,sha256=Sqa-PPElwdUKxdh9BbU5MSqnf_i7BFqANrzVUXYUNuQ,1380
64
65
  dexcontrol/utils/subscribers/base.py,sha256=t4zcps_kjFG1wj6WSrZ4HJg0Bxcdnu-C8NkVVupmNVg,9769
65
66
  dexcontrol/utils/subscribers/camera.py,sha256=0kaeoKjKCxRQ5iaKK3r_94xEaVxNDBAMd2ZHL6X1kWU,11201
@@ -69,7 +70,7 @@ dexcontrol/utils/subscribers/imu.py,sha256=Us4ZWzLfqujg16jvrNIoHcvUyxbRCv6mZlaAi
69
70
  dexcontrol/utils/subscribers/lidar.py,sha256=OlmORIf-dBO4LV4u6pWccoxh-nYTAtGlIfhjj-sG1F4,5691
70
71
  dexcontrol/utils/subscribers/protobuf.py,sha256=gtE2b9ZtR2UXftKA5nX7bvTLkj8AeXDYZMqe4B3t5BQ,3696
71
72
  dexcontrol/utils/subscribers/rtc.py,sha256=mxD-IIeQwluvq0_D63lQJJEXrJsIc6yxX7uD0PDh08k,11350
72
- dexcontrol-0.2.12.dist-info/METADATA,sha256=3O9vIN5nR0U_mpL2QAb7Enr_eRzUbSI3Zk49Vv_HFpI,36840
73
- dexcontrol-0.2.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
74
- dexcontrol-0.2.12.dist-info/licenses/LICENSE,sha256=0J2KCMNNnW5WZPK5x8xUiCxApBf7h83693ggSJYiue0,31745
75
- dexcontrol-0.2.12.dist-info/RECORD,,
73
+ dexcontrol-0.3.0.dist-info/METADATA,sha256=S7GeiNE2J1A3YutnF1x98mnAczFc_DqmLOo_3nnOHXo,37239
74
+ dexcontrol-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
75
+ dexcontrol-0.3.0.dist-info/licenses/LICENSE,sha256=0J2KCMNNnW5WZPK5x8xUiCxApBf7h83693ggSJYiue0,31745
76
+ dexcontrol-0.3.0.dist-info/RECORD,,