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,4 +1,4 @@
1
- so100:
1
+ two_pandas:
2
2
  observation_types:
3
3
  - pixels_agent_pos
4
4
  available_scenes:
@@ -7,85 +7,245 @@ so100:
7
7
  - pickandplace
8
8
  action_space:
9
9
  actuator_names:
10
- - shoulder_pan
11
- - shoulder_lift
12
- - elbow_flex
13
- - wrist_flex
14
- - wrist_roll
15
- - gripper
10
+ - L_joint1
11
+ - L_joint2
12
+ - L_joint3
13
+ - L_joint4
14
+ - L_joint5
15
+ - L_joint6
16
+ - L_joint7
17
+ - L_gripper
18
+ - R_joint1
19
+ - R_joint2
20
+ - R_joint3
21
+ - R_joint4
22
+ - R_joint5
23
+ - R_joint6
24
+ - R_joint7
25
+ - R_gripper
16
26
  actuator_limits:
17
- - name: shoulder_pan
18
- lower: -2.2
19
- upper: 2.2
20
- - name: shoulder_lift
21
- lower: -3.14158
22
- upper: 0.2
23
- - name: elbow_flex
27
+ - name: L_joint1
28
+ lower: -2.8973
29
+ upper: 2.8973
30
+ - name: L_joint2
31
+ lower: -1.7628
32
+ upper: 1.7628
33
+ - name: L_joint3
34
+ lower: -2.8973
35
+ upper: 2.8973
36
+ - name: L_joint4
37
+ lower: -3.07179
38
+ upper: -0.06979
39
+ - name: L_joint5
40
+ lower: -2.8973
41
+ upper: 2.8973
42
+ - name: L_joint6
43
+ lower: -0.31421
44
+ upper: 3.45579
45
+ - name: L_joint7
46
+ lower: -3.6826
47
+ upper: 2.112
48
+ - name: L_gripper
24
49
  lower: 0.0
25
- upper: 3.14158
26
- - name: wrist_flex
27
- lower: -2.0
28
- upper: 1.8
29
- - name: wrist_roll
30
- lower: -3.14158
31
- upper: 3.14158
32
- - name: gripper
33
- lower: -0.2
34
- upper: 2.0
50
+ upper: 255.0
51
+ - name: R_joint1
52
+ lower: -2.8973
53
+ upper: 2.8973
54
+ - name: R_joint2
55
+ lower: -1.7628
56
+ upper: 1.7628
57
+ - name: R_joint3
58
+ lower: -2.8973
59
+ upper: 2.8973
60
+ - name: R_joint4
61
+ lower: -3.07179
62
+ upper: -0.06979
63
+ - name: R_joint5
64
+ lower: -2.8973
65
+ upper: 2.8973
66
+ - name: R_joint6
67
+ lower: -0.31421
68
+ upper: 3.45579
69
+ - name: R_joint7
70
+ lower: -3.6826
71
+ upper: 2.112
72
+ - name: R_gripper
73
+ lower: 0.0
74
+ upper: 255.0
35
75
  observation_space:
36
76
  actuator_names:
37
- - shoulder_pan
38
- - shoulder_lift
39
- - elbow_flex
40
- - wrist_flex
41
- - wrist_roll
42
- - gripper
77
+ - L_joint1
78
+ - L_joint2
79
+ - L_joint3
80
+ - L_joint4
81
+ - L_joint5
82
+ - L_joint6
83
+ - L_joint7
84
+ - L_gripper
85
+ - R_joint1
86
+ - R_joint2
87
+ - R_joint3
88
+ - R_joint4
89
+ - R_joint5
90
+ - R_joint6
91
+ - R_joint7
92
+ - R_gripper
43
93
  actuator_limits:
44
- - name: shoulder_pan
45
- lower: -2.2
46
- upper: 2.2
47
- - name: shoulder_lift
48
- lower: -3.14158
49
- upper: 0.2
50
- - name: elbow_flex
94
+ - name: L_joint1
95
+ lower: -2.8973
96
+ upper: 2.8973
97
+ - name: L_joint2
98
+ lower: -1.7628
99
+ upper: 1.7628
100
+ - name: L_joint3
101
+ lower: -2.8973
102
+ upper: 2.8973
103
+ - name: L_joint4
104
+ lower: -3.07179
105
+ upper: -0.06979
106
+ - name: L_joint5
107
+ lower: -2.8973
108
+ upper: 2.8973
109
+ - name: L_joint6
110
+ lower: -0.31421
111
+ upper: 3.45579
112
+ - name: L_joint7
113
+ lower: -3.6826
114
+ upper: 2.112
115
+ - name: L_gripper
51
116
  lower: 0.0
52
- upper: 3.14158
53
- - name: wrist_flex
54
- lower: -2.0
55
- upper: 1.8
56
- - name: wrist_roll
57
- lower: -3.14158
58
- upper: 3.14158
59
- - name: gripper
60
- lower: -0.2
61
- upper: 2.0
62
- camera_config:
63
- laptop:
64
- camera_index: 0
65
- fps: 60
66
- camera_resolution: [640, 480]
67
- phone:
68
- camera_index: 1
69
- fps: 60
70
- camera_resolution: [640, 480]
71
-
72
- stretch_v1:
73
- observation_types:
74
- - pixels_agent_pos
75
- available_scenes:
76
- - loft
77
- available_tasks:
78
- - pickandplace
79
- - navigation
80
- action_space:
81
- observation_space:
117
+ upper: 255.0
118
+ - name: R_joint1
119
+ lower: -2.8973
120
+ upper: 2.8973
121
+ - name: R_joint2
122
+ lower: -1.7628
123
+ upper: 1.7628
124
+ - name: R_joint3
125
+ lower: -2.8973
126
+ upper: 2.8973
127
+ - name: R_joint4
128
+ lower: -3.07179
129
+ upper: -0.06979
130
+ - name: R_joint5
131
+ lower: -2.8973
132
+ upper: 2.8973
133
+ - name: R_joint6
134
+ lower: -0.31421
135
+ upper: 3.45579
136
+ - name: R_joint7
137
+ lower: -3.6826
138
+ upper: 2.112
139
+ - name: R_gripper
140
+ lower: 0.0
141
+ upper: 255.0
82
142
 
83
- dji300:
143
+ unitreego1:
84
144
  observation_types:
85
- - pixels_agent_pos
145
+ - agent_pos
86
146
  available_scenes:
87
- - drone_flight
147
+ - velocity
88
148
  available_tasks:
89
- - navigation
149
+ - locomotion
90
150
  action_space:
151
+ actuator_names:
152
+ - FR_hip
153
+ - FR_thigh
154
+ - FR_calf
155
+ - FL_hip
156
+ - FL_thigh
157
+ - FL_calf
158
+ - RR_hip
159
+ - RR_thigh
160
+ - RR_calf
161
+ - RL_hip
162
+ - RL_thigh
163
+ - RL_calf
164
+ actuator_limits:
165
+ - name: FR_hip
166
+ lower: -0.863
167
+ upper: 0.863
168
+ - name: FR_thigh
169
+ lower: -0.686
170
+ upper: 4.501
171
+ - name: FR_calf
172
+ lower: -2.818
173
+ upper: -0.888
174
+ - name: FL_hip
175
+ lower: -0.863
176
+ upper: 0.863
177
+ - name: FL_thigh
178
+ lower: -0.686
179
+ upper: 4.501
180
+ - name: FL_calf
181
+ lower: -2.818
182
+ upper: -0.888
183
+ - name: RR_hip
184
+ lower: -0.863
185
+ upper: 0.863
186
+ - name: RR_thigh
187
+ lower: -0.686
188
+ upper: 4.501
189
+ - name: RR_calf
190
+ lower: -2.818
191
+ upper: -0.888
192
+ - name: RL_hip
193
+ lower: -0.863
194
+ upper: 0.863
195
+ - name: RL_thigh
196
+ lower: -0.686
197
+ upper: 4.501
198
+ - name: RL_calf
199
+ lower: -2.818
200
+ upper: -0.888
91
201
  observation_space:
202
+ actuator_names:
203
+ - FR_hip
204
+ - FR_thigh
205
+ - FR_calf
206
+ - FL_hip
207
+ - FL_thigh
208
+ - FL_calf
209
+ - RR_hip
210
+ - RR_thigh
211
+ - RR_calf
212
+ - RL_hip
213
+ - RL_thigh
214
+ - RL_calf
215
+ actuator_limits:
216
+ - name: FR_hip
217
+ lower: -0.863
218
+ upper: 0.863
219
+ - name: FR_thigh
220
+ lower: -0.686
221
+ upper: 4.501
222
+ - name: FR_calf
223
+ lower: -2.818
224
+ upper: -0.888
225
+ - name: FL_hip
226
+ lower: -0.863
227
+ upper: 0.863
228
+ - name: FL_thigh
229
+ lower: -0.686
230
+ upper: 4.501
231
+ - name: FL_calf
232
+ lower: -2.818
233
+ upper: -0.888
234
+ - name: RR_hip
235
+ lower: -0.863
236
+ upper: 0.863
237
+ - name: RR_thigh
238
+ lower: -0.686
239
+ upper: 4.501
240
+ - name: RR_calf
241
+ lower: -2.818
242
+ upper: -0.888
243
+ - name: RL_hip
244
+ lower: -0.863
245
+ upper: 0.863
246
+ - name: RL_thigh
247
+ lower: -0.686
248
+ upper: 4.501
249
+ - name: RL_calf
250
+ lower: -2.818
251
+ upper: -0.888
@@ -0,0 +1,23 @@
1
+ """Engine lifecycle management for LuckyEngine."""
2
+
3
+ from .check_updates import check_updates
4
+ from .download import apply_changes, get_base_url, get_os_type
5
+ from .manager import (
6
+ find_luckyengine_executable,
7
+ is_luckyengine_running,
8
+ launch_luckyengine,
9
+ stop_luckyengine,
10
+ )
11
+
12
+ __all__ = [
13
+ # Manager functions
14
+ "launch_luckyengine",
15
+ "stop_luckyengine",
16
+ "is_luckyengine_running",
17
+ "find_luckyengine_executable",
18
+ # Update functions
19
+ "check_updates",
20
+ "apply_changes",
21
+ "get_base_url",
22
+ "get_os_type",
23
+ ]
@@ -1,21 +1,37 @@
1
+ """
2
+ Check for LuckyEngine executable updates.
3
+
4
+ This module compares local and remote file structures to detect changes.
5
+ """
6
+
1
7
  import json
8
+ import logging
2
9
  import mimetypes
3
10
  import os
4
11
  import platform
5
12
  import re
6
13
  import sys
7
- import time
8
14
  import zlib
15
+ from typing import Optional
9
16
  from urllib.parse import urljoin
10
17
 
11
18
  import requests
12
19
 
13
- root_path = os.path.join(os.path.dirname(__file__), "..", "..", "examples/Binary/mac")
14
- json_file = os.path.join(root_path, "file_structure.json")
15
- base_url = "https://builds.luckyrobots.xyz/"
20
+ logger = logging.getLogger("luckyrobots.engine.check_updates")
21
+
22
+ BASE_URL = "https://builds.luckyrobots.xyz/"
16
23
 
17
24
 
18
- def get_os_type():
25
+ def get_os_type() -> str:
26
+ """
27
+ Get the operating system type as a string.
28
+
29
+ Returns:
30
+ "mac", "win", or "linux"
31
+
32
+ Raises:
33
+ ValueError: If the OS is not supported.
34
+ """
19
35
  os_type = platform.system().lower()
20
36
  if os_type == "darwin":
21
37
  return "mac"
@@ -27,9 +43,18 @@ def get_os_type():
27
43
  raise ValueError(f"Unsupported operating system: {os_type}")
28
44
 
29
45
 
30
- def calculate_crc32(file_path):
46
+ def calculate_crc32(file_path: str) -> int:
47
+ """
48
+ Calculate CRC32 checksum for a file.
49
+
50
+ Args:
51
+ file_path: Path to the file.
52
+
53
+ Returns:
54
+ CRC32 checksum as unsigned 32-bit integer.
55
+ """
56
+ crc32 = 0
31
57
  with open(file_path, "rb") as file:
32
- crc32 = 0
33
58
  while True:
34
59
  data = file.read(65536) # Read in 64kb chunks
35
60
  if not data:
@@ -38,7 +63,16 @@ def calculate_crc32(file_path):
38
63
  return crc32 & 0xFFFFFFFF # Ensure unsigned 32-bit integer
39
64
 
40
65
 
41
- def scan_directory(root_path):
66
+ def scan_directory(root_path: str) -> list[dict]:
67
+ """
68
+ Scan a directory and create a file structure representation.
69
+
70
+ Args:
71
+ root_path: Root directory to scan.
72
+
73
+ Returns:
74
+ List of dictionaries containing file/directory metadata.
75
+ """
42
76
  file_structure = []
43
77
 
44
78
  for dirpath, dirnames, filenames in os.walk(root_path):
@@ -50,7 +84,7 @@ def scan_directory(root_path):
50
84
  {
51
85
  "path": relative_path,
52
86
  "type": "directory",
53
- "size": 0, # Directories don't have a size in this context
87
+ "size": 0,
54
88
  "mtime": os.path.getmtime(dir_path),
55
89
  "crc32": 0,
56
90
  "mime_type": "directory",
@@ -64,9 +98,9 @@ def scan_directory(root_path):
64
98
  file_stat = os.stat(file_path)
65
99
 
66
100
  # Guess the file type using mimetypes
67
- file_type, encoding = mimetypes.guess_type(file_path)
101
+ file_type, _ = mimetypes.guess_type(file_path)
68
102
  if file_type is None:
69
- file_type = "application/octet-stream" # Default to binary if type can't be guessed
103
+ file_type = "application/octet-stream"
70
104
 
71
105
  file_structure.append(
72
106
  {
@@ -82,17 +116,28 @@ def scan_directory(root_path):
82
116
  return file_structure
83
117
 
84
118
 
85
- def save_json(data, filename):
119
+ def save_json(data: dict, filename: str) -> None:
120
+ """Save data to a JSON file."""
86
121
  with open(filename, "w") as f:
87
122
  json.dump(data, f, indent=2)
88
123
 
89
124
 
90
- def load_json(filename):
125
+ def load_json(filename: str) -> dict:
126
+ """Load data from a JSON file."""
91
127
  with open(filename, "r") as f:
92
128
  return json.load(f)
93
129
 
94
130
 
95
- def clean_path(path):
131
+ def clean_path(path: str) -> tuple[str, Optional[str]]:
132
+ """
133
+ Clean and parse a path string.
134
+
135
+ Args:
136
+ path: Path string to clean.
137
+
138
+ Returns:
139
+ Tuple of (directory_path, attribute_name).
140
+ """
96
141
  # Remove ['children'] and quotes, replace '][' with '/'
97
142
  cleaned = re.sub(r"\['children'\]", "", path).replace("']['", "/").strip("[]'")
98
143
  # Replace remaining single quotes with nothing
@@ -102,9 +147,21 @@ def clean_path(path):
102
147
  return parts[0], parts[1] if len(parts) > 1 else None
103
148
 
104
149
 
105
- def compare_structures(json1, json2):
106
- dict1 = {item["path"]: item for item in json1}
107
- dict2 = {item["path"]: item for item in json2}
150
+ def compare_structures(
151
+ client_structure: list[dict], server_structure: list[dict]
152
+ ) -> list[dict]:
153
+ """
154
+ Compare two file structures and return list of changes.
155
+
156
+ Args:
157
+ client_structure: Local file structure.
158
+ server_structure: Remote file structure.
159
+
160
+ Returns:
161
+ List of changes (new, modified, deleted files).
162
+ """
163
+ dict1 = {item["path"]: item for item in client_structure}
164
+ dict2 = {item["path"]: item for item in server_structure}
108
165
 
109
166
  result = []
110
167
 
@@ -116,7 +173,6 @@ def compare_structures(json1, json2):
116
173
  elif item["type"] == "file" and item["crc32"] != dict1[path]["crc32"]:
117
174
  item["change_type"] = "modified"
118
175
  result.append(item)
119
- # Unchanged items are not added to the result
120
176
 
121
177
  # Check for deleted items
122
178
  for path, item in dict1.items():
@@ -124,13 +180,19 @@ def compare_structures(json1, json2):
124
180
  item["change_type"] = "deleted"
125
181
  result.append(item)
126
182
 
127
- # Remove the item with "path": "hashmap.json" from the result
183
+ # Remove hashmap.json from changes (it's metadata)
128
184
  result = [item for item in result if item["path"] != "hashmap.json"]
129
- # save_json(result, "changes.json")
185
+
130
186
  return result
131
187
 
132
188
 
133
- def scan_server(server_path):
189
+ def scan_server(server_path: str) -> None:
190
+ """
191
+ Scan server directory structure and save hashmap files.
192
+
193
+ Args:
194
+ server_path: Path to server root directory.
195
+ """
134
196
  mac_path = os.path.join(server_path, "mac")
135
197
  win_path = os.path.join(server_path, "win")
136
198
  linux_path = os.path.join(server_path, "linux")
@@ -144,53 +206,50 @@ def scan_server(server_path):
144
206
  save_json(linux_structure, os.path.join(server_path, "linux/hashmap.json"))
145
207
 
146
208
 
147
- def check_updates(root_path):
148
- # Determine the operating system
149
- os_type = get_os_type()
150
- global base_url
151
- # Set the base URL
209
+ def check_updates(root_path: str, base_url: Optional[str] = None) -> list[dict]:
210
+ """
211
+ Check for updates by comparing local and remote file structures.
212
+
213
+ Args:
214
+ root_path: Local root directory to check.
215
+ base_url: Base URL for remote server (uses default if None).
216
+
217
+ Returns:
218
+ List of changes detected.
219
+ """
220
+ if base_url is None:
221
+ base_url = BASE_URL
152
222
 
153
- # Construct the URL based on the operating system
154
223
  os_type = get_os_type()
155
224
  url = urljoin(base_url, f"{os_type}/hashmap.json")
156
225
 
157
226
  # Download the JSON file
158
227
  try:
159
- response = requests.get(url)
160
- response.raise_for_status() # Raise an exception for HTTP errors
228
+ response = requests.get(url, timeout=10)
229
+ response.raise_for_status()
161
230
  server_structure = response.json()
162
231
  except requests.RequestException as e:
163
- print(f"Error downloading JSON file: {e}")
232
+ logger.warning(f"Error downloading JSON file: {e}")
164
233
  server_structure = None
165
234
 
166
235
  if server_structure is None:
167
- print("Using local scan as no remote file could be downloaded.")
236
+ logger.info("Using local scan as no remote file could be downloaded.")
168
237
  server_structure = {}
169
238
 
170
239
  # Scan the directory and create a new file structure
171
240
  client_structure = scan_directory(root_path)
172
241
 
173
- # If a previous scan exists, compare and show changes
242
+ # Compare and return changes
174
243
  if server_structure:
175
244
  changes = compare_structures(client_structure, server_structure)
176
-
177
- # for future debugging
178
- # # Write the flat JSON to a file
179
- # with open('changes.json', 'w') as f:
180
- # json.dump(changes, f, indent=2)
181
-
182
- # print(f"Changes have been written to changes.json")
245
+ return changes
183
246
  else:
184
- print("No server structure available for comparison.")
185
-
186
- # for future debugging
187
- # # Save the new structure
188
- # save_json(client_structure, "./client_structure.json")
189
-
190
- return changes
247
+ logger.info("No server structure available for comparison.")
248
+ return []
191
249
 
192
250
 
193
251
  if __name__ == "__main__":
252
+ # This is used as a cron job on the main server to keep file structures up to date
194
253
  lr_server_root = None
195
254
 
196
255
  for arg in sys.argv:
@@ -199,6 +258,7 @@ if __name__ == "__main__":
199
258
  break
200
259
 
201
260
  if lr_server_root:
202
- # this is being used as a cron job on the main server to keep the client file structures up to date
203
- print(f"Scanning server at {lr_server_root}")
261
+ logger.info(f"Scanning server at {lr_server_root}")
204
262
  scan_server(lr_server_root)
263
+ else:
264
+ logger.error("--lr-server-root argument required")