luckyrobots 0.1.66__py3-none-any.whl → 0.1.72__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.
- luckyrobots/__init__.py +30 -12
- luckyrobots/client.py +997 -0
- luckyrobots/config/robots.yaml +231 -71
- luckyrobots/engine/__init__.py +23 -0
- luckyrobots/{utils → engine}/check_updates.py +108 -48
- luckyrobots/{utils → engine}/download.py +61 -39
- luckyrobots/engine/manager.py +427 -0
- luckyrobots/grpc/__init__.py +6 -0
- luckyrobots/grpc/generated/__init__.py +18 -0
- luckyrobots/grpc/generated/agent_pb2.py +69 -0
- luckyrobots/grpc/generated/agent_pb2_grpc.py +283 -0
- luckyrobots/grpc/generated/camera_pb2.py +47 -0
- luckyrobots/grpc/generated/camera_pb2_grpc.py +144 -0
- luckyrobots/grpc/generated/common_pb2.py +43 -0
- luckyrobots/grpc/generated/common_pb2_grpc.py +24 -0
- luckyrobots/grpc/generated/hazel_rpc_pb2.py +43 -0
- luckyrobots/grpc/generated/hazel_rpc_pb2_grpc.py +24 -0
- luckyrobots/grpc/generated/media_pb2.py +39 -0
- luckyrobots/grpc/generated/media_pb2_grpc.py +24 -0
- luckyrobots/grpc/generated/mujoco_pb2.py +51 -0
- luckyrobots/grpc/generated/mujoco_pb2_grpc.py +230 -0
- luckyrobots/grpc/generated/scene_pb2.py +66 -0
- luckyrobots/grpc/generated/scene_pb2_grpc.py +317 -0
- luckyrobots/grpc/generated/telemetry_pb2.py +47 -0
- luckyrobots/grpc/generated/telemetry_pb2_grpc.py +143 -0
- luckyrobots/grpc/generated/viewport_pb2.py +50 -0
- luckyrobots/grpc/generated/viewport_pb2_grpc.py +144 -0
- luckyrobots/grpc/proto/agent.proto +213 -0
- luckyrobots/grpc/proto/camera.proto +41 -0
- luckyrobots/grpc/proto/common.proto +36 -0
- luckyrobots/grpc/proto/hazel_rpc.proto +32 -0
- luckyrobots/grpc/proto/media.proto +26 -0
- luckyrobots/grpc/proto/mujoco.proto +64 -0
- luckyrobots/grpc/proto/scene.proto +104 -0
- luckyrobots/grpc/proto/telemetry.proto +43 -0
- luckyrobots/grpc/proto/viewport.proto +45 -0
- luckyrobots/luckyrobots.py +252 -0
- luckyrobots/models/__init__.py +15 -0
- luckyrobots/models/camera.py +97 -0
- luckyrobots/models/observation.py +135 -0
- luckyrobots/models/randomization.py +77 -0
- luckyrobots/{utils/helpers.py → utils.py} +75 -40
- luckyrobots-0.1.72.dist-info/METADATA +262 -0
- luckyrobots-0.1.72.dist-info/RECORD +47 -0
- {luckyrobots-0.1.66.dist-info → luckyrobots-0.1.72.dist-info}/WHEEL +1 -1
- luckyrobots/core/luckyrobots.py +0 -624
- luckyrobots/core/manager.py +0 -236
- luckyrobots/core/models.py +0 -68
- luckyrobots/core/node.py +0 -273
- luckyrobots/message/__init__.py +0 -18
- luckyrobots/message/pubsub.py +0 -145
- luckyrobots/message/srv/client.py +0 -81
- luckyrobots/message/srv/service.py +0 -135
- luckyrobots/message/srv/types.py +0 -83
- luckyrobots/message/transporter.py +0 -427
- luckyrobots/utils/event_loop.py +0 -94
- luckyrobots/utils/sim_manager.py +0 -406
- luckyrobots-0.1.66.dist-info/METADATA +0 -253
- luckyrobots-0.1.66.dist-info/RECORD +0 -24
- {luckyrobots-0.1.66.dist-info → luckyrobots-0.1.72.dist-info}/licenses/LICENSE +0 -0
luckyrobots/config/robots.yaml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
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
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
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:
|
|
18
|
-
lower: -2.
|
|
19
|
-
upper: 2.
|
|
20
|
-
- name:
|
|
21
|
-
lower: -
|
|
22
|
-
upper:
|
|
23
|
-
- name:
|
|
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:
|
|
26
|
-
- name:
|
|
27
|
-
lower: -2.
|
|
28
|
-
upper:
|
|
29
|
-
- name:
|
|
30
|
-
lower: -
|
|
31
|
-
upper:
|
|
32
|
-
- name:
|
|
33
|
-
lower: -
|
|
34
|
-
upper: 2.
|
|
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
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
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:
|
|
45
|
-
lower: -2.
|
|
46
|
-
upper: 2.
|
|
47
|
-
- name:
|
|
48
|
-
lower: -
|
|
49
|
-
upper:
|
|
50
|
-
- name:
|
|
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:
|
|
53
|
-
- name:
|
|
54
|
-
lower: -2.
|
|
55
|
-
upper:
|
|
56
|
-
- name:
|
|
57
|
-
lower: -
|
|
58
|
-
upper:
|
|
59
|
-
- name:
|
|
60
|
-
lower: -
|
|
61
|
-
upper: 2.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
143
|
+
unitreego1:
|
|
84
144
|
observation_types:
|
|
85
|
-
-
|
|
145
|
+
- agent_pos
|
|
86
146
|
available_scenes:
|
|
87
|
-
-
|
|
147
|
+
- velocity
|
|
88
148
|
available_tasks:
|
|
89
|
-
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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,
|
|
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,
|
|
101
|
+
file_type, _ = mimetypes.guess_type(file_path)
|
|
68
102
|
if file_type is None:
|
|
69
|
-
file_type = "application/octet-stream"
|
|
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(
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
183
|
+
# Remove hashmap.json from changes (it's metadata)
|
|
128
184
|
result = [item for item in result if item["path"] != "hashmap.json"]
|
|
129
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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()
|
|
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
|
-
|
|
232
|
+
logger.warning(f"Error downloading JSON file: {e}")
|
|
164
233
|
server_structure = None
|
|
165
234
|
|
|
166
235
|
if server_structure is None:
|
|
167
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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")
|