luckyrobots 0.1.68__py3-none-any.whl → 0.1.70__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 (57) hide show
  1. luckyrobots/__init__.py +23 -12
  2. luckyrobots/client.py +800 -0
  3. luckyrobots/config/robots.yaml +231 -71
  4. luckyrobots/engine/__init__.py +23 -0
  5. luckyrobots/{utils → engine}/check_updates.py +108 -48
  6. luckyrobots/{utils → engine}/download.py +61 -39
  7. luckyrobots/engine/manager.py +427 -0
  8. luckyrobots/grpc/__init__.py +6 -0
  9. luckyrobots/grpc/generated/__init__.py +18 -0
  10. luckyrobots/grpc/generated/agent_pb2.py +61 -0
  11. luckyrobots/grpc/generated/agent_pb2_grpc.py +255 -0
  12. luckyrobots/grpc/generated/camera_pb2.py +45 -0
  13. luckyrobots/grpc/generated/camera_pb2_grpc.py +155 -0
  14. luckyrobots/grpc/generated/common_pb2.py +39 -0
  15. luckyrobots/grpc/generated/common_pb2_grpc.py +27 -0
  16. luckyrobots/grpc/generated/media_pb2.py +35 -0
  17. luckyrobots/grpc/generated/media_pb2_grpc.py +27 -0
  18. luckyrobots/grpc/generated/mujoco_pb2.py +47 -0
  19. luckyrobots/grpc/generated/mujoco_pb2_grpc.py +248 -0
  20. luckyrobots/grpc/generated/scene_pb2.py +54 -0
  21. luckyrobots/grpc/generated/scene_pb2_grpc.py +248 -0
  22. luckyrobots/grpc/generated/telemetry_pb2.py +43 -0
  23. luckyrobots/grpc/generated/telemetry_pb2_grpc.py +154 -0
  24. luckyrobots/grpc/generated/viewport_pb2.py +48 -0
  25. luckyrobots/grpc/generated/viewport_pb2_grpc.py +155 -0
  26. luckyrobots/grpc/proto/agent.proto +152 -0
  27. luckyrobots/grpc/proto/camera.proto +41 -0
  28. luckyrobots/grpc/proto/common.proto +36 -0
  29. luckyrobots/grpc/proto/hazel_rpc.proto +32 -0
  30. luckyrobots/grpc/proto/media.proto +26 -0
  31. luckyrobots/grpc/proto/mujoco.proto +64 -0
  32. luckyrobots/grpc/proto/scene.proto +70 -0
  33. luckyrobots/grpc/proto/telemetry.proto +43 -0
  34. luckyrobots/grpc/proto/viewport.proto +45 -0
  35. luckyrobots/luckyrobots.py +212 -0
  36. luckyrobots/models/__init__.py +13 -0
  37. luckyrobots/models/camera.py +97 -0
  38. luckyrobots/models/observation.py +135 -0
  39. luckyrobots/{utils/helpers.py → utils.py} +75 -40
  40. luckyrobots-0.1.70.dist-info/METADATA +262 -0
  41. luckyrobots-0.1.70.dist-info/RECORD +44 -0
  42. {luckyrobots-0.1.68.dist-info → luckyrobots-0.1.70.dist-info}/WHEEL +1 -1
  43. luckyrobots/core/luckyrobots.py +0 -628
  44. luckyrobots/core/manager.py +0 -236
  45. luckyrobots/core/models.py +0 -68
  46. luckyrobots/core/node.py +0 -273
  47. luckyrobots/message/__init__.py +0 -18
  48. luckyrobots/message/pubsub.py +0 -145
  49. luckyrobots/message/srv/client.py +0 -81
  50. luckyrobots/message/srv/service.py +0 -135
  51. luckyrobots/message/srv/types.py +0 -83
  52. luckyrobots/message/transporter.py +0 -427
  53. luckyrobots/utils/event_loop.py +0 -94
  54. luckyrobots/utils/sim_manager.py +0 -413
  55. luckyrobots-0.1.68.dist-info/METADATA +0 -253
  56. luckyrobots-0.1.68.dist-info/RECORD +0 -24
  57. {luckyrobots-0.1.68.dist-info → luckyrobots-0.1.70.dist-info}/licenses/LICENSE +0 -0
@@ -1,42 +1,56 @@
1
+ """
2
+ Download and update LuckyEngine executable files.
3
+
4
+ This module handles downloading updates and applying changes to the LuckyEngine binary.
5
+ """
6
+
7
+ import logging
1
8
  import os
2
9
  import platform
3
- import re
4
- import sys
5
- import zipfile
6
- from datetime import datetime
10
+ from typing import Optional
7
11
 
8
12
  import requests
9
- from bs4 import BeautifulSoup
10
13
  from tqdm import tqdm
11
14
 
12
15
  from .check_updates import check_updates
13
16
 
14
- base_url = "https://builds.luckyrobots.xyz/"
17
+ logger = logging.getLogger("luckyrobots.engine.download")
15
18
 
19
+ BASE_URL = "https://builds.luckyrobots.xyz/"
16
20
 
17
- def get_base_url():
18
- import requests
19
- from requests.exceptions import RequestException
20
21
 
21
- def is_server_active(url):
22
- try:
23
- response = requests.get(url, timeout=1)
24
- return response.status_code == 200
25
- except RequestException:
26
- return False
22
+ def get_base_url() -> str:
23
+ """
24
+ Get the base URL for downloads, checking local server first.
27
25
 
26
+ Returns:
27
+ Base URL string (local or remote).
28
+ """
28
29
  local_url = "http://192.168.1.148/builds"
29
30
  remote_url = "https://builds.luckyrobots.xyz"
30
31
 
31
- if is_server_active(local_url):
32
- print("Using local server:", local_url)
33
- return local_url
34
- else:
35
- print("Using remote server:", remote_url)
36
- return remote_url
32
+ try:
33
+ response = requests.get(local_url, timeout=1)
34
+ if response.status_code == 200:
35
+ logger.info(f"Using local server: {local_url}")
36
+ return local_url
37
+ except requests.RequestException:
38
+ pass
39
+
40
+ logger.info(f"Using remote server: {remote_url}")
41
+ return remote_url
42
+
43
+
44
+ def get_os_type() -> str:
45
+ """
46
+ Get the operating system type as a string.
37
47
 
48
+ Returns:
49
+ "mac", "win", or "linux"
38
50
 
39
- def get_os_type():
51
+ Raises:
52
+ ValueError: If the OS is not supported.
53
+ """
40
54
  os_type = platform.system().lower()
41
55
  if os_type == "darwin":
42
56
  return "mac"
@@ -48,32 +62,40 @@ def get_os_type():
48
62
  raise ValueError(f"Unsupported operating system: {os_type}")
49
63
 
50
64
 
51
- def apply_changes(changes):
52
- # Iterate through changes and handle each change type
65
+ def apply_changes(changes: list[dict], binary_path: str = "./Binary") -> None:
66
+ """
67
+ Apply changes by downloading new/modified files and deleting removed files.
68
+
69
+ Args:
70
+ changes: List of change dictionaries with 'change_type', 'path', etc.
71
+ binary_path: Base path where binary files are stored.
72
+ """
73
+ base_url = get_base_url()
74
+ os_type = get_os_type()
75
+
53
76
  for item in changes:
54
- if item["change_type"] in ["modified", "new_file"]:
55
- # Check if the item is a directory
77
+ change_type = item.get("change_type")
78
+ item_path = os.path.join(binary_path, item["path"])
79
+
80
+ if change_type in ["modified", "new_file"]:
56
81
  if item.get("type") == "directory":
57
82
  # Create the directory
58
- item_path = os.path.join("./Binary", item["path"])
59
83
  os.makedirs(item_path, exist_ok=True)
60
- print(f"Created directory: {item_path}")
84
+ logger.debug(f"Created directory: {item_path}")
61
85
  else:
62
86
  # Handle file download with progress bar
63
- os_type = get_os_type()
64
87
  file_url = f"{base_url}{os_type}/{item['path']}"
65
88
 
66
89
  # Ensure the directory exists
67
- item_path = os.path.join("./Binary", item["path"])
68
90
  item_dir = os.path.dirname(item_path)
69
91
  os.makedirs(item_dir, exist_ok=True)
70
92
 
71
93
  # Download the file with progress bar
72
94
  try:
73
- response = requests.get(file_url, stream=True)
95
+ response = requests.get(file_url, stream=True, timeout=30)
74
96
  response.raise_for_status()
75
97
  total_size = int(response.headers.get("content-length", 0))
76
- # " ▁▂▃▄▅▆▇█"
98
+
77
99
  with open(item_path, "wb") as f, tqdm(
78
100
  desc=f"{item['path'][:8]}...{item['path'][-16:]}",
79
101
  total=total_size,
@@ -85,19 +107,19 @@ def apply_changes(changes):
85
107
  for data in response.iter_content(chunk_size=1024):
86
108
  size = f.write(data)
87
109
  progress_bar.update(size)
88
- # print(f"Downloaded: {item_path}")
110
+
111
+ logger.debug(f"Downloaded: {item_path}")
89
112
  except requests.RequestException as e:
90
- print(f"Error downloading {item_path}: {e}")
113
+ logger.error(f"Error downloading {item_path}: {e}")
91
114
 
92
- elif item["change_type"] == "deleted":
115
+ elif change_type == "deleted":
93
116
  # Delete the file or directory
94
- item_path = os.path.join("./Binary", item["path"])
95
117
  try:
96
118
  if os.path.isdir(item_path):
97
119
  os.rmdir(item_path)
98
- print(f"Deleted directory: {item_path}")
120
+ logger.debug(f"Deleted directory: {item_path}")
99
121
  else:
100
122
  os.remove(item_path)
101
- print(f"Deleted file: {item_path}")
123
+ logger.debug(f"Deleted file: {item_path}")
102
124
  except OSError as e:
103
- print(f"Error deleting {item_path}: {e}")
125
+ logger.error(f"Error deleting {item_path}: {e}")
@@ -0,0 +1,427 @@
1
+ """
2
+ LuckyEngine engine lifecycle management.
3
+
4
+ This module handles launching, stopping, and managing the LuckyEngine executable.
5
+ """
6
+
7
+ import logging
8
+ import os
9
+ import platform
10
+ import subprocess
11
+ import tempfile
12
+ import threading
13
+ import time
14
+ from typing import Optional
15
+
16
+ logger = logging.getLogger("luckyrobots.luckyengine")
17
+
18
+ # Module-level state
19
+ LOCK_FILE = os.path.join(tempfile.gettempdir(), "luckyengine_lock")
20
+ _process: Optional[subprocess.Popen] = None
21
+ _monitor_thread: Optional[threading.Thread] = None
22
+ _shutdown_event = threading.Event()
23
+
24
+
25
+ # ============================================================================
26
+ # Public API
27
+ # ============================================================================
28
+
29
+
30
+ def find_luckyengine_executable() -> Optional[str]:
31
+ """
32
+ Automatically find LuckyEngine executable in common locations.
33
+
34
+ Checks in order:
35
+ 1. LUCKYENGINE_PATH environment variable
36
+ 2. LUCKYENGINE_HOME environment variable
37
+ 3. System installation paths
38
+
39
+ Returns:
40
+ Path to executable if found, None otherwise.
41
+ """
42
+ is_wsl = "microsoft" in platform.uname().release.lower()
43
+
44
+ # 1. Check LUCKYENGINE_PATH environment variable (highest priority)
45
+ env_path = os.environ.get("LUCKYENGINE_PATH")
46
+ if env_path:
47
+ logger.info(f"Using LUCKYENGINE_PATH environment variable: {env_path}")
48
+ if os.path.exists(env_path):
49
+ return env_path
50
+ logger.warning(f"LUCKYENGINE_PATH points to non-existent file: {env_path}")
51
+
52
+ # 2. Check LUCKYENGINE_HOME environment variable
53
+ env_home = os.environ.get("LUCKYENGINE_HOME")
54
+ if env_home:
55
+ logger.info(f"Using LUCKYENGINE_HOME environment variable: {env_home}")
56
+ executable = _get_executable_for_platform(env_home, "LuckyEngine", is_wsl)
57
+ if executable and os.path.exists(executable):
58
+ return executable
59
+ logger.warning(f"LUCKYENGINE_HOME does not contain executable: {executable}")
60
+
61
+ # 3. System installation paths
62
+ system_paths = _get_system_paths(is_wsl)
63
+ for path in system_paths:
64
+ if os.path.exists(path):
65
+ logger.info(f"Found LuckyEngine at: {path}")
66
+ return path
67
+
68
+ return None
69
+
70
+
71
+ def is_luckyengine_running() -> bool:
72
+ """
73
+ Check if LuckyEngine is currently running.
74
+
75
+ Returns:
76
+ True if lock file exists, False otherwise.
77
+ """
78
+ return os.path.exists(LOCK_FILE)
79
+
80
+
81
+ def launch_luckyengine(
82
+ scene: str = "ArmLevel",
83
+ robot: str = "so100",
84
+ task: Optional[str] = None,
85
+ executable_path: Optional[str] = None,
86
+ headless: bool = False,
87
+ windowed: bool = True,
88
+ verbose: bool = False,
89
+ ) -> bool:
90
+ """
91
+ Launch LuckyEngine with the specified parameters.
92
+
93
+ Args:
94
+ scene: Scene name to load.
95
+ robot: Robot name to spawn.
96
+ task: Optional task name.
97
+ executable_path: Path to executable (auto-detected if None).
98
+ headless: Run without rendering.
99
+ windowed: Run in windowed mode (vs fullscreen).
100
+ verbose: Show engine output.
101
+
102
+ Returns:
103
+ True if launch succeeded, False otherwise.
104
+ """
105
+ global _process, _monitor_thread
106
+
107
+ # Check if already running
108
+ if is_luckyengine_running():
109
+ logger.error(
110
+ "LuckyEngine is already running. "
111
+ "Stop the existing instance or remove the lock file at "
112
+ f"{LOCK_FILE}"
113
+ )
114
+ return False
115
+
116
+ # Find executable if not provided
117
+ if executable_path is None:
118
+ executable_path = find_luckyengine_executable()
119
+ if executable_path is None:
120
+ logger.error(
121
+ "Could not find LuckyEngine executable. "
122
+ "Please provide the path manually."
123
+ )
124
+ logger.info("You can set the path using environment variables:")
125
+ logger.info(" LUCKYENGINE_PATH=/full/path/to/LuckyEngine.exe")
126
+ logger.info(" LUCKYENGINE_HOME=/path/to/luckyengine/directory")
127
+ return False
128
+
129
+ if not os.path.exists(executable_path):
130
+ logger.error(f"Executable not found at: {executable_path}")
131
+ return False
132
+
133
+ try:
134
+ # Set execute permissions on Unix systems
135
+ if platform.system() != "Windows":
136
+ os.chmod(executable_path, 0o755)
137
+
138
+ logger.info(f"Launching LuckyEngine: {executable_path}")
139
+
140
+ # Build command
141
+ command = [executable_path]
142
+ command.append(f"-Scene={scene}")
143
+ command.append(f"-Robot={robot}")
144
+
145
+ if task:
146
+ command.append(f"-Task={task}")
147
+
148
+ if headless:
149
+ command.append("-Headless")
150
+ else:
151
+ if windowed:
152
+ command.append("-windowed")
153
+ else:
154
+ command.append("-fullscreen")
155
+
156
+ command.append("-Realtime")
157
+
158
+ logger.info(f"Command: {' '.join(command)}")
159
+
160
+ # Launch process
161
+ if platform.system() == "Windows":
162
+ DETACHED_PROCESS = 0x00000008
163
+ _process = subprocess.Popen(
164
+ command,
165
+ creationflags=DETACHED_PROCESS,
166
+ close_fds=True,
167
+ stdout=None if verbose else subprocess.DEVNULL,
168
+ stderr=None if verbose else subprocess.DEVNULL,
169
+ )
170
+ else:
171
+ _process = subprocess.Popen(
172
+ command,
173
+ start_new_session=True,
174
+ stdout=None if verbose else subprocess.DEVNULL,
175
+ stderr=None if verbose else subprocess.DEVNULL,
176
+ )
177
+
178
+ # Start monitoring
179
+ _shutdown_event.clear()
180
+ _monitor_thread = threading.Thread(target=monitor_process, daemon=True)
181
+ _monitor_thread.start()
182
+
183
+ create_lock_file(_process.pid)
184
+
185
+ logger.info(f"LuckyEngine started successfully (PID: {_process.pid})")
186
+ logger.info(f"Scene: {scene}, Robot: {robot}, Task: {task or 'None'}")
187
+
188
+ return True
189
+
190
+ except Exception as e:
191
+ logger.error(f"Failed to launch LuckyEngine: {e}")
192
+ stop_luckyengine()
193
+ return False
194
+
195
+
196
+ def stop_luckyengine() -> bool:
197
+ """
198
+ Stop the running LuckyEngine instance.
199
+
200
+ Returns:
201
+ True if stopped successfully, False otherwise.
202
+ """
203
+ global _process
204
+
205
+ if not is_luckyengine_running():
206
+ logger.info("LuckyEngine is not running")
207
+ return True
208
+
209
+ try:
210
+ if _process:
211
+ logger.info("Stopping LuckyEngine...")
212
+
213
+ try:
214
+ _process.terminate()
215
+ _process.wait(timeout=5)
216
+ except subprocess.TimeoutExpired:
217
+ logger.info("Graceful termination timeout, using kill_processes...")
218
+ except Exception:
219
+ logger.info("Graceful termination failed, using kill_processes...")
220
+
221
+ kill_processes()
222
+
223
+ remove_lock_file()
224
+ return True
225
+ except Exception as e:
226
+ logger.error(f"Error stopping LuckyEngine: {e}")
227
+ return False
228
+
229
+
230
+ # ============================================================================
231
+ # Private Helpers
232
+ # ============================================================================
233
+
234
+
235
+ def _get_executable_for_platform(
236
+ home_dir: str, base_name: str, is_wsl: bool
237
+ ) -> Optional[str]:
238
+ """Get the executable path for the current platform."""
239
+ if platform.system() == "Linux" and not is_wsl:
240
+ return os.path.join(home_dir, f"{base_name}.sh")
241
+ elif platform.system() == "Darwin":
242
+ return os.path.join(
243
+ home_dir, f"{base_name}.app", "Contents", "MacOS", base_name
244
+ )
245
+ else: # Windows or WSL2
246
+ return os.path.join(home_dir, f"{base_name}.exe")
247
+
248
+
249
+ def _get_system_paths(is_wsl: bool) -> list[str]:
250
+ """Get system installation paths for the current platform."""
251
+ paths = []
252
+
253
+ if platform.system() == "Linux" and not is_wsl:
254
+ paths.extend(
255
+ [
256
+ "/opt/LuckyEngine/LuckyEngine.sh",
257
+ "/usr/local/bin/LuckyEngine.sh",
258
+ os.path.expanduser("~/.local/share/LuckyEngine/LuckyEngine.sh"),
259
+ os.path.expanduser("~/LuckyEngine/LuckyEngine.sh"),
260
+ ]
261
+ )
262
+ elif platform.system() == "Darwin":
263
+ paths.extend(
264
+ [
265
+ "/Applications/LuckyEngine/LuckyEngine.app/Contents/MacOS/LuckyEngine",
266
+ os.path.expanduser(
267
+ "~/Applications/LuckyEngine/LuckyEngine.app/Contents/MacOS/LuckyEngine"
268
+ ),
269
+ ]
270
+ )
271
+ else: # Windows or WSL2
272
+ paths.extend(
273
+ [
274
+ "C:\\Program Files\\LuckyEngine\\LuckyEngine.exe",
275
+ "C:\\Program Files (x86)\\LuckyEngine\\LuckyEngine.exe",
276
+ os.path.expanduser("~\\AppData\\Local\\LuckyEngine\\LuckyEngine.exe"),
277
+ ]
278
+ )
279
+
280
+ if is_wsl:
281
+ paths.extend(
282
+ [
283
+ "/mnt/c/Program Files/LuckyEngine/LuckyEngine.exe",
284
+ "/mnt/c/Program Files (x86)/LuckyEngine/LuckyEngine.exe",
285
+ ]
286
+ )
287
+
288
+ return paths
289
+
290
+
291
+ def monitor_process() -> None:
292
+ """Monitor the LuckyEngine process and handle its termination."""
293
+ global _process
294
+ try:
295
+ while not _shutdown_event.is_set():
296
+ if _process is None or _process.poll() is not None:
297
+ logger.info("LuckyEngine process has terminated")
298
+ break
299
+ time.sleep(1)
300
+ except Exception as e:
301
+ logger.error(f"Error in process monitor: {e}")
302
+ finally:
303
+ # Ensure cleanup happens when monitoring stops
304
+ if not _shutdown_event.is_set():
305
+ remove_lock_file()
306
+
307
+
308
+ def create_lock_file(pid: int) -> None:
309
+ """Create a lock file with the process ID."""
310
+ with open(LOCK_FILE, "w") as f:
311
+ f.write(str(pid))
312
+
313
+
314
+ def remove_lock_file() -> None:
315
+ """Remove the lock file."""
316
+ try:
317
+ if os.path.exists(LOCK_FILE):
318
+ os.remove(LOCK_FILE)
319
+ logger.debug("Lock file removed successfully")
320
+ else:
321
+ logger.debug("Lock file doesn't exist, nothing to remove")
322
+ except Exception as e:
323
+ logger.error(f"Error removing lock file: {e}")
324
+
325
+
326
+ def kill_processes() -> None:
327
+ """Kill all LuckyEngine processes."""
328
+ system = platform.system()
329
+ is_wsl = "microsoft" in platform.uname().release.lower()
330
+
331
+ if is_wsl:
332
+ _kill_wsl_processes()
333
+ elif system == "Windows":
334
+ _kill_windows_processes()
335
+ elif system == "Darwin":
336
+ _kill_macos_processes()
337
+ else:
338
+ _kill_linux_processes()
339
+
340
+
341
+ def _kill_wsl_processes() -> None:
342
+ """Kill LuckyEngine processes on WSL."""
343
+ try:
344
+ result = subprocess.run(
345
+ [
346
+ "/mnt/c/Windows/System32/taskkill.exe",
347
+ "/F",
348
+ "/IM",
349
+ "LuckyEngine.exe",
350
+ ],
351
+ capture_output=True,
352
+ text=True,
353
+ timeout=10,
354
+ )
355
+ if result.returncode == 0:
356
+ logger.info("Killed LuckyEngine.exe processes")
357
+ elif result.returncode == 128: # Process not found
358
+ logger.debug("No LuckyEngine processes found running")
359
+ except Exception as e:
360
+ logger.debug(f"Error killing WSL processes: {e}")
361
+
362
+
363
+ def _kill_windows_processes() -> None:
364
+ """Kill LuckyEngine processes on Windows."""
365
+ try:
366
+ result = subprocess.run(
367
+ ["taskkill", "/F", "/IM", "LuckyEngine.exe"],
368
+ capture_output=True,
369
+ text=True,
370
+ timeout=10,
371
+ )
372
+ if result.returncode == 0:
373
+ logger.info("Successfully killed LuckyEngine.exe processes")
374
+ elif result.returncode == 128: # Process not found
375
+ logger.info("No LuckyEngine processes found")
376
+ except Exception as e:
377
+ logger.debug(f"Error killing Windows processes: {e}")
378
+
379
+
380
+ def _kill_macos_processes() -> None:
381
+ """Kill LuckyEngine processes on macOS."""
382
+ try:
383
+ result = subprocess.run(
384
+ ["pkill", "-f", "LuckyEngine"],
385
+ capture_output=True,
386
+ text=True,
387
+ timeout=10,
388
+ )
389
+
390
+ if result.returncode == 0:
391
+ logger.info("Successfully killed LuckyEngine processes")
392
+ elif result.returncode == 1: # No processes found
393
+ logger.info("No LuckyEngine processes found")
394
+ else:
395
+ logger.warning(f"pkill failed: {result.stderr}")
396
+
397
+ except subprocess.TimeoutExpired:
398
+ logger.error("pkill command timed out")
399
+ except FileNotFoundError:
400
+ logger.error("pkill command not found")
401
+ except Exception as e:
402
+ logger.error(f"Failed to kill macOS processes: {e}")
403
+
404
+
405
+ def _kill_linux_processes() -> None:
406
+ """Kill LuckyEngine processes on Linux."""
407
+ try:
408
+ result = subprocess.run(
409
+ ["pkill", "-f", "LuckyEngine"],
410
+ capture_output=True,
411
+ text=True,
412
+ timeout=10,
413
+ )
414
+
415
+ if result.returncode == 0:
416
+ logger.info("Successfully killed LuckyEngine processes")
417
+ elif result.returncode == 1: # No processes found
418
+ logger.info("No LuckyEngine processes found")
419
+ else:
420
+ logger.warning(f"pkill failed: {result.stderr}")
421
+
422
+ except subprocess.TimeoutExpired:
423
+ logger.error("pkill command timed out")
424
+ except FileNotFoundError:
425
+ logger.error("pkill command not found")
426
+ except Exception as e:
427
+ logger.error(f"Failed to kill Linux processes: {e}")
@@ -0,0 +1,6 @@
1
+ """
2
+ gRPC generated stubs for LuckyEngine.
3
+
4
+ This module contains the generated protobuf stubs. The client implementation
5
+ is in luckyrobots/client.py.
6
+ """
@@ -0,0 +1,18 @@
1
+ """Checked-in generated gRPC stubs for the LuckyEngine `.proto` files.
2
+
3
+ Regenerate with:
4
+ python -m grpc_tools.protoc -I src/luckyrobots/rpc/proto \
5
+ --python_out=src/luckyrobots/rpc/generated \
6
+ --grpc_python_out=src/luckyrobots/rpc/generated \
7
+ src/luckyrobots/rpc/proto/common.proto \
8
+ src/luckyrobots/rpc/proto/media.proto \
9
+ src/luckyrobots/rpc/proto/scene.proto \
10
+ src/luckyrobots/rpc/proto/mujoco.proto \
11
+ src/luckyrobots/rpc/proto/telemetry.proto \
12
+ src/luckyrobots/rpc/proto/agent.proto \
13
+ src/luckyrobots/rpc/proto/viewport.proto \
14
+ src/luckyrobots/rpc/proto/camera.proto
15
+
16
+ Then adjust imports in the generated `*_pb2.py` and `*_pb2_grpc.py` to be
17
+ package-relative (`from . import ...`).
18
+ """
@@ -0,0 +1,61 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: agent.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+
13
+ _runtime_version.ValidateProtobufRuntimeVersion(
14
+ _runtime_version.Domain.PUBLIC, 6, 31, 1, "", "agent.proto"
15
+ )
16
+ # @@protoc_insertion_point(imports)
17
+
18
+ _sym_db = _symbol_database.Default()
19
+
20
+
21
+ from . import common_pb2 as common__pb2
22
+ from . import media_pb2 as media__pb2
23
+ from . import mujoco_pb2 as mujoco__pb2
24
+ from . import telemetry_pb2 as telemetry__pb2
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
28
+ b'\n\x0b\x61gent.proto\x12\x0chazel.rpc.v1\x1a\x0c\x63ommon.proto\x1a\x0bmedia.proto\x1a\x0cmujoco.proto\x1a\x0ftelemetry.proto"\x81\x01\n\x0b\x41gentSchema\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x19\n\x11observation_names\x18\x02 \x03(\t\x12\x14\n\x0c\x61\x63tion_names\x18\x03 \x03(\t\x12\x18\n\x10observation_size\x18\x04 \x01(\r\x12\x13\n\x0b\x61\x63tion_size\x18\x05 \x01(\r"+\n\x15GetAgentSchemaRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t"C\n\x16GetAgentSchemaResponse\x12)\n\x06schema\x18\x01 \x01(\x0b\x32\x19.hazel.rpc.v1.AgentSchema"<\n\x12StreamAgentRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x12\n\ntarget_fps\x18\x02 \x01(\r"\'\n\x11ResetAgentRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t"6\n\x12ResetAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t"\x87\x01\n\nAgentFrame\x12\x14\n\x0ctimestamp_ms\x18\x01 \x01(\x04\x12\x14\n\x0c\x66rame_number\x18\x02 \x01(\r\x12\x14\n\x0cobservations\x18\x03 \x03(\x02\x12\x0f\n\x07\x61\x63tions\x18\x04 \x03(\x02\x12\x12\n\nagent_name\x18\x05 \x01(\t\x12\x12\n\ntarget_fps\x18\x06 \x01(\r"\x8a\x01\n\x15GetCameraFrameRequest\x12$\n\x02id\x18\x01 \x01(\x0b\x32\x16.hazel.rpc.v1.EntityIdH\x00\x12\x0e\n\x04name\x18\x02 \x01(\tH\x00\x12\r\n\x05width\x18\x03 \x01(\r\x12\x0e\n\x06height\x18\x04 \x01(\r\x12\x0e\n\x06\x66ormat\x18\x05 \x01(\tB\x0c\n\nidentifier"_\n\x17GetViewportFrameRequest\x12\x15\n\rviewport_name\x18\x01 \x01(\t\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x0e\n\x06\x66ormat\x18\x04 \x01(\t"\x84\x02\n\x15GetObservationRequest\x12\x12\n\nrobot_name\x18\x01 \x01(\t\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x1b\n\x13include_joint_state\x18\x03 \x01(\x08\x12\x1b\n\x13include_agent_frame\x18\x04 \x01(\x08\x12\x19\n\x11include_telemetry\x18\x05 \x01(\x08\x12\x34\n\x07\x63\x61meras\x18\x06 \x03(\x0b\x32#.hazel.rpc.v1.GetCameraFrameRequest\x12\x38\n\tviewports\x18\x07 \x03(\x0b\x32%.hazel.rpc.v1.GetViewportFrameRequest"\xe3\x02\n\x16GetObservationResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x14\n\x0ctimestamp_ms\x18\x03 \x01(\x04\x12\x14\n\x0c\x66rame_number\x18\x04 \x01(\r\x12-\n\x0bjoint_state\x18\x05 \x01(\x0b\x32\x18.hazel.rpc.v1.JointState\x12-\n\x0b\x61gent_frame\x18\x06 \x01(\x0b\x32\x18.hazel.rpc.v1.AgentFrame\x12/\n\ttelemetry\x18\x07 \x01(\x0b\x32\x1c.hazel.rpc.v1.TelemetryFrame\x12\x34\n\rcamera_frames\x18\x08 \x03(\x0b\x32\x1d.hazel.rpc.v1.NamedImageFrame\x12\x36\n\x0fviewport_frames\x18\t \x03(\x0b\x32\x1d.hazel.rpc.v1.NamedImageFrame2\xe6\x02\n\x0c\x41gentService\x12[\n\x0eGetAgentSchema\x12#.hazel.rpc.v1.GetAgentSchemaRequest\x1a$.hazel.rpc.v1.GetAgentSchemaResponse\x12[\n\x0eGetObservation\x12#.hazel.rpc.v1.GetObservationRequest\x1a$.hazel.rpc.v1.GetObservationResponse\x12K\n\x0bStreamAgent\x12 .hazel.rpc.v1.StreamAgentRequest\x1a\x18.hazel.rpc.v1.AgentFrame0\x01\x12O\n\nResetAgent\x12\x1f.hazel.rpc.v1.ResetAgentRequest\x1a .hazel.rpc.v1.ResetAgentResponseB\x03\xf8\x01\x01\x62\x06proto3'
29
+ )
30
+
31
+ _globals = globals()
32
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
33
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "agent_pb2", _globals)
34
+ if not _descriptor._USE_C_DESCRIPTORS:
35
+ _globals["DESCRIPTOR"]._loaded_options = None
36
+ _globals["DESCRIPTOR"]._serialized_options = b"\370\001\001"
37
+ _globals["_AGENTSCHEMA"]._serialized_start = 88
38
+ _globals["_AGENTSCHEMA"]._serialized_end = 217
39
+ _globals["_GETAGENTSCHEMAREQUEST"]._serialized_start = 219
40
+ _globals["_GETAGENTSCHEMAREQUEST"]._serialized_end = 262
41
+ _globals["_GETAGENTSCHEMARESPONSE"]._serialized_start = 264
42
+ _globals["_GETAGENTSCHEMARESPONSE"]._serialized_end = 331
43
+ _globals["_STREAMAGENTREQUEST"]._serialized_start = 333
44
+ _globals["_STREAMAGENTREQUEST"]._serialized_end = 393
45
+ _globals["_RESETAGENTREQUEST"]._serialized_start = 395
46
+ _globals["_RESETAGENTREQUEST"]._serialized_end = 434
47
+ _globals["_RESETAGENTRESPONSE"]._serialized_start = 436
48
+ _globals["_RESETAGENTRESPONSE"]._serialized_end = 490
49
+ _globals["_AGENTFRAME"]._serialized_start = 493
50
+ _globals["_AGENTFRAME"]._serialized_end = 628
51
+ _globals["_GETCAMERAFRAMEREQUEST"]._serialized_start = 631
52
+ _globals["_GETCAMERAFRAMEREQUEST"]._serialized_end = 769
53
+ _globals["_GETVIEWPORTFRAMEREQUEST"]._serialized_start = 771
54
+ _globals["_GETVIEWPORTFRAMEREQUEST"]._serialized_end = 866
55
+ _globals["_GETOBSERVATIONREQUEST"]._serialized_start = 869
56
+ _globals["_GETOBSERVATIONREQUEST"]._serialized_end = 1129
57
+ _globals["_GETOBSERVATIONRESPONSE"]._serialized_start = 1132
58
+ _globals["_GETOBSERVATIONRESPONSE"]._serialized_end = 1487
59
+ _globals["_AGENTSERVICE"]._serialized_start = 1490
60
+ _globals["_AGENTSERVICE"]._serialized_end = 1848
61
+ # @@protoc_insertion_point(module_scope)