autoverse-cli 0.28.1__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 (51) hide show
  1. autoverse_cli-0.28.1.dist-info/METADATA +91 -0
  2. autoverse_cli-0.28.1.dist-info/RECORD +51 -0
  3. autoverse_cli-0.28.1.dist-info/WHEEL +5 -0
  4. autoverse_cli-0.28.1.dist-info/entry_points.txt +2 -0
  5. autoverse_cli-0.28.1.dist-info/licenses/LICENSE +33 -0
  6. autoverse_cli-0.28.1.dist-info/top_level.txt +1 -0
  7. avrs/__init__.py +0 -0
  8. avrs/app_version.py +24 -0
  9. avrs/argparse_help.py +30 -0
  10. avrs/avrs.py +183 -0
  11. avrs/can_tool.py +192 -0
  12. avrs/can_tool_util.py +190 -0
  13. avrs/cfg.py +78 -0
  14. avrs/launcher.py +256 -0
  15. avrs/launcher_util.py +203 -0
  16. avrs/race_cloud.py +506 -0
  17. avrs/race_cloud_bridge_can.py +100 -0
  18. avrs/race_cloud_cfg_util.py +310 -0
  19. avrs/race_cloud_fwd_api.py +49 -0
  20. avrs/race_cloud_util.py +384 -0
  21. avrs/requests/change_camera.py +24 -0
  22. avrs/requests/code_booz.py +69 -0
  23. avrs/requests/demo.py +19 -0
  24. avrs/requests/dump_sim_config.py +14 -0
  25. avrs/requests/environment.py +46 -0
  26. avrs/requests/fault_injection.py +186 -0
  27. avrs/requests/get_object_config.py +18 -0
  28. avrs/requests/get_web_viz_meta.py +11 -0
  29. avrs/requests/leaderboard.py +74 -0
  30. avrs/requests/list_sim_objects.py +26 -0
  31. avrs/requests/log_path.py +28 -0
  32. avrs/requests/misc.py +70 -0
  33. avrs/requests/move_to_landmark.py +16 -0
  34. avrs/requests/npc.py +289 -0
  35. avrs/requests/race_control.py +48 -0
  36. avrs/requests/request.py +61 -0
  37. avrs/requests/reset_to_track.py +21 -0
  38. avrs/requests/rest_request.py +45 -0
  39. avrs/requests/restart.py +12 -0
  40. avrs/requests/scenario_control.py +43 -0
  41. avrs/requests/spawn_object.py +44 -0
  42. avrs/requests/teleport.py +40 -0
  43. avrs/requests/toggle_hud.py +11 -0
  44. avrs/requests/vd.py +64 -0
  45. avrs/requests/vehicle_input.py +21 -0
  46. avrs/requests/vehicle_replay.py +454 -0
  47. avrs/shell_completion.py +121 -0
  48. avrs/simconfig.py +75 -0
  49. avrs/simconfig_util.py +170 -0
  50. avrs/tests.py +9 -0
  51. avrs/util.py +13 -0
avrs/can_tool_util.py ADDED
@@ -0,0 +1,190 @@
1
+ import can
2
+ import cantools
3
+ import time
4
+ import os
5
+ import datetime
6
+ import threading
7
+ import queue
8
+ import json
9
+ import math
10
+ import statistics
11
+
12
+ def get_eav24_hl0(throttle, gear, brake):
13
+ return {
14
+ "HL_TargetThrottle": throttle,
15
+ "HL_TargetGear": gear,
16
+ "HL_TargetPressure_RR": brake,
17
+ "HL_TargetPressure_RL": brake,
18
+ "HL_TargetPressure_FR": brake,
19
+ "HL_TargetPressure_FL": brake,
20
+ "HL_Alive_01": 0
21
+ }
22
+
23
+ def get_eav24_hl2(steer):
24
+ return {
25
+ "HL_Alive_02": 0,
26
+ "HL_PSA_Profile_Vel_rad_s": 0,
27
+ "HL_PSA_Profile_Dec_rad_s2": 0,
28
+ "HL_PSA_Profile_Acc_rad_s2": 0,
29
+ "HL_TargetPSAControl": steer,
30
+ "HL_PSA_ModeOfOperation": 0
31
+ }
32
+
33
+ def get_eav24_msigs(dbc, throttle, gear, brake, steer):
34
+ return {
35
+ dbc.get_message_by_name('HL_Msg_01'): get_eav24_hl0(throttle, gear, brake),
36
+ dbc.get_message_by_name('HL_Msg_02'): get_eav24_hl2(steer)
37
+ }
38
+
39
+ def send_eav24_can_values(can_name, dbc, throttle, gear, brake, steer, duration, rate = 100):
40
+ with can.interface.Bus(can_name, bustype='socketcan') as bus:
41
+ message_1 = dbc.get_message_by_name('HL_Msg_01')
42
+ message_2 = dbc.get_message_by_name('HL_Msg_02')
43
+ print('sending EAV24 CAN inputs on {} with throttle {}, gear {}, brake {}, steer {}, for {} seconds'.format(can_name, throttle, gear, brake, steer, duration))
44
+ signals_1 = get_eav24_hl0(throttle, gear, brake)
45
+ signals_2 = get_eav24_hl2(steer)
46
+
47
+ msigs = get_eav24_msigs(dbc, throttle, gear, brake, steer)
48
+ send_eav24_can_signals(bus, msigs, rate, duration)
49
+
50
+ def send_eav24_can_signals(bus, message_signals, rate, duration):
51
+ start_time = time.time()
52
+ while time.time() - start_time < duration:
53
+ for msg, sig in message_signals.items():
54
+ bus.send(can.Message(arbitration_id=msg.frame_id, data=msg.encode(sig), is_extended_id=False))
55
+ time.sleep(1.0 / rate)
56
+
57
+ def test_eav24_can_latency(can_name, dbc, nsamples, send_rate, duration, save_report_as):
58
+ with can.interface.Bus(can_name, bustype='socketcan') as bus:
59
+ delays = []
60
+ times = []
61
+
62
+ for i in range(nsamples):
63
+ print('test {}'.format(i))
64
+ start_time = time.time()
65
+ last_throttle_time = time.time()
66
+ last_throttle_value = 50;
67
+
68
+ msigs = get_eav24_msigs(dbc, last_throttle_value, 1, 0, 0)
69
+
70
+ send_thread = threading.Thread(target=send_eav24_can_signals, args=(bus, msigs, send_rate, duration))
71
+ send_thread.start()
72
+
73
+ is_back_to_zero = False
74
+ while not is_back_to_zero:
75
+ message = bus.recv()
76
+ if message is not None:
77
+ decoded = decode_can_message(dbc, message)
78
+ if decoded and decoded['name'] == 'ICE_Status_01':
79
+ ack_value = decoded['data']['ICE_TargetThrottle_ACK']
80
+ if math.isclose(ack_value, last_throttle_value):
81
+ t = time.time()
82
+ #print('got throttle ack matching command at t{}'.format(t - last_throttle_time))
83
+ delay = t - last_throttle_time
84
+ delays.append(delay)
85
+ times.append(t)
86
+ print('got throttle ack for command {} after {} seconds'.format(last_throttle_value, delay))
87
+ if last_throttle_value == 0:
88
+ is_back_to_zero = True
89
+ send_thread.join()
90
+ else:
91
+ last_throttle_value = 0
92
+ msigs = get_eav24_msigs(dbc, last_throttle_value, 1, 0, 0)
93
+ send_thread.join()
94
+ send_thread = threading.Thread(target=send_eav24_can_signals, args=(bus, msigs, send_rate, duration))
95
+ send_thread.start()
96
+ last_throttle_time = time.time()
97
+
98
+ # Plot results
99
+ avg_delay = statistics.mean(delays)
100
+ print('avg delay {}, min delay {}, max dealy {}'.format(avg_delay, min(delays), max(delays)))
101
+
102
+ if save_report_as != '':
103
+ print('saving report to {}'.format(save_report_as))
104
+ report = {
105
+ 'average_delay': avg_delay,
106
+ 'min_delay': min(delays),
107
+ 'max_delay': max(delays),
108
+ 'num_samples': nsamples
109
+ }
110
+ with open(save_report_as, 'w', encoding='utf-8') as f:
111
+ json.dump(report, f, ensure_ascii=False, indent=4)
112
+
113
+
114
+ def echo_can(can_names, dbcs, duration, isolate, do_rates, save_report_as):
115
+ start_time = time.time()
116
+ report = {}
117
+ rates = {}
118
+ print_interval = 1.0
119
+ last_print = start_time
120
+
121
+ jobs = []
122
+ print_lock = threading.Lock()
123
+ for n in can_names:
124
+ echo_thread = threading.Thread(target=echo_can_job, args=(n, dbcs, duration, isolate, do_rates, rates, print_lock))
125
+ echo_thread.start()
126
+ jobs.append(echo_thread)
127
+
128
+ while time.time() - start_time < duration:
129
+ t = time.time()
130
+ if do_rates and t - last_print > print_interval:
131
+ last_print = t
132
+ for r in rates:
133
+ print('{} rate: {}hz ({} samples)'.format(r, 1.0 / rates[r]['rate'], rates[r]['ct']))
134
+ print('=' * 15)
135
+
136
+ for j in jobs:
137
+ j.join()
138
+
139
+ if do_rates and save_report_as != '':
140
+ print('saving report to {}'.format(save_report_as))
141
+ with open(save_report_as, 'w', encoding='utf-8') as f:
142
+ json.dump(rates, f, ensure_ascii=False, indent=4)
143
+
144
+ def echo_can_job(can_name, dbcs, duration, isolate, do_rates, out_rate_data, print_lock):
145
+ start_time = time.time()
146
+ with can.interface.Bus(can_name, bustype='socketcan') as bus:
147
+ while time.time() - start_time < duration:
148
+ message = bus.recv()
149
+ if message is not None:
150
+ for db in dbcs:
151
+ decoded = decode_can_message(db, message)
152
+ if decoded is not None and (len(isolate) == 0 or decoded['name'] in isolate):
153
+ update_can_rate_stats(decoded, out_rate_data, start_time)
154
+ if not do_rates and print_lock.acquire(True):
155
+ print(decoded)
156
+ print_lock.release()
157
+
158
+ def decode_can_message(dbc, message):
159
+ try:
160
+ decoded_message = dbc.decode_message(message.arbitration_id, message.data)
161
+ message_name = dbc.get_message_by_frame_id(message.arbitration_id).name
162
+ timestamp = datetime.datetime.now().isoformat()
163
+ return {'timestamp': timestamp, 'name': message_name, 'data': decoded_message}
164
+ except KeyError:
165
+ return None
166
+
167
+ def update_can_rate_stats(message, rates, start_time):
168
+ if not message['name'] in rates:
169
+ rates[message['name']] = {
170
+ 'prev_t': start_time,
171
+ 'rate': time.time() - start_time,
172
+ 'ct': 0,
173
+ 'min': 100000000,
174
+ 'max': 0.0000001
175
+ }
176
+ r = rates[message['name']]['rate']
177
+ t = rates[message['name']]['prev_t']
178
+ ct = rates[message['name']]['ct'] + 1
179
+ dt = time.time() - t
180
+ rate_diff = dt - r
181
+ rates[message['name']]['rate'] = r + (1.0 / ct) * rate_diff
182
+ rates[message['name']]['ct'] = ct
183
+ rates[message['name']]['prev_t'] = time.time()
184
+
185
+ this_rate = 1.0 / dt
186
+ if this_rate > rates[message['name']]['max']:
187
+ rates[message['name']]['max'] = this_rate
188
+ elif this_rate < rates[message['name']]['min']:
189
+ rates[message['name']]['min'] = this_rate
190
+
avrs/cfg.py ADDED
@@ -0,0 +1,78 @@
1
+ import json
2
+ import os
3
+ import shutil
4
+
5
+ def get_cfg_dir(cli_name):
6
+ return os.path.join(os.environ['HOME'], '.config', cli_name)
7
+
8
+ def get_cfg_file(cli_name):
9
+ return os.path.join(get_cfg_dir(cli_name), 'config.json')
10
+
11
+ def load_cfg(cli_name):
12
+ cfg_dir = get_cfg_dir(cli_name)
13
+ if not os.path.exists(cfg_dir):
14
+ os.makedirs(cfg_dir)
15
+
16
+ cfg_path = get_cfg_file(cli_name)
17
+ cfg = {}
18
+
19
+ if os.path.exists(cfg_path):
20
+ with open(cfg_path, 'r', encoding='utf-8') as f:
21
+ cfg = json.load(f)
22
+ return cfg
23
+
24
+ def save_cfg(cli_name, cfg):
25
+ cfg_path = get_cfg_file(cli_name)
26
+ with open(cfg_path, 'w', encoding='utf-8') as f:
27
+ json.dump(cfg, f, ensure_ascii=False, indent=4)
28
+
29
+ # Save a file to be re-used later to a cached location
30
+ def add_cached_file(cli_name, category, file_to_cache, overwrite_ok):
31
+ if not os.path.exists(file_to_cache):
32
+ return (False, '{} is not a valid file'.format(file_to_cache))
33
+ cfg = load_cfg(cli_name)
34
+ if '__cached_files__' not in cfg:
35
+ cfg['__cached_files__'] = {}
36
+ if category not in cfg['__cached_files__']:
37
+ cfg['__cached_files__'][category] = []
38
+ file_to_cache_name = os.path.basename(file_to_cache)
39
+ cfg['__cached_files__'][category].append(file_to_cache_name)
40
+ cat_path = os.path.join(get_cfg_dir(cli_name), category)
41
+ if not os.path.exists(cat_path):
42
+ os.makedirs(cat_path)
43
+ cache_path = os.path.join(cat_path, file_to_cache_name)
44
+ if os.path.isfile(cache_path) and not overwrite_ok:
45
+ return (False, '{} already exists and overwrite not specified as ok'.format(cache_path))
46
+ shutil.copyfile(file_to_cache, cache_path)
47
+ save_cfg(cli_name, cfg)
48
+ return (True, '{} cached to {}'.format(file_to_cache, cache_path))
49
+
50
+ # Get a file previously cached
51
+ def get_cached_file(cli_name, category, file_to_get):
52
+ cfg = load_cfg(cli_name)
53
+ if '__cached_files__' not in cfg:
54
+ return (False, 'no cached files')
55
+ if category not in cfg['__cached_files__']:
56
+ return (False, 'cached file {} not found'.format(file_to_get))
57
+ cache_path = os.path.join(get_cfg_dir(cli_name), category, file_to_get)
58
+ cf = cfg['__cached_files__'][category]
59
+ # Check by index
60
+ try:
61
+ file_index = int(file_to_get)
62
+ if file_index > -1 and file_index < len(cf):
63
+ cache_path = os.path.join(get_cfg_dir(cli_name), category, cf[file_index])
64
+ else:
65
+ return (False, '{} is not a valid file index'.format(file_index))
66
+ except Exception as e:
67
+ pass
68
+ if not os.path.exists(cache_path):
69
+ return (False, 'failed to get cached file at {}'.format(cache_path))
70
+ return (True, cache_path)
71
+
72
+ def get_cached_file_list(cli_name, category):
73
+ cfg = load_cfg(cli_name)
74
+ if '__cached_files__' not in cfg:
75
+ return []
76
+ if category not in cfg['__cached_files__']:
77
+ return []
78
+ return cfg['__cached_files__'][category]
avrs/launcher.py ADDED
@@ -0,0 +1,256 @@
1
+ import os
2
+ import stat
3
+ import json
4
+ import logging
5
+ import http.client
6
+ import boto3
7
+ import sys
8
+ import shutil
9
+ from avrs.cfg import *
10
+ from avrs.launcher_util import *
11
+
12
+ class AvrsLauncher:
13
+ def __init__(self, parent_parser, cfg):
14
+ self.cfg = cfg
15
+ self.logger = logging.getLogger('avrs')
16
+ provision_parser = parent_parser.add_parser(
17
+ 'launcher',
18
+ help='launcher operations, such as license registration or updating a sim install\n\n')
19
+ sps = provision_parser.add_subparsers(required=True, help='launcher options')
20
+
21
+ register_license_psr = sps.add_parser(
22
+ 'register-license',
23
+ help='registers a user license, allowing certain functionaility')
24
+ register_license_psr.add_argument(
25
+ 'license_path',
26
+ help='the path to the license file to register')
27
+ register_license_psr.set_defaults(func=self.register_license)
28
+
29
+ license_status_psr = sps.add_parser(
30
+ 'license-status',
31
+ help='shows information related to a user license')
32
+ license_status_psr.set_defaults(func=self.license_status)
33
+
34
+ get_latest_psr = sps.add_parser(
35
+ 'get-latest-sim-version',
36
+ help='ask the build info api for the latest sim version')
37
+ get_latest_psr.add_argument("variant", help="which variant to get the latest verison of")
38
+ get_latest_psr.set_defaults(func=self.get_latest_sim_version)
39
+
40
+ get_variants_psr = sps.add_parser(
41
+ 'list-variants',
42
+ help='lists the available variants of the simulator that can be downloaded')
43
+ get_variants_psr.set_defaults(func=self.get_variants)
44
+
45
+ download_sim_psr = sps.add_parser('download-simulator', help='download the simulator')
46
+ download_sim_psr.add_argument('variant', help='which variant of the simulator to download (eg, \"a2rl-iron\" or \"a2rl-humble\")')
47
+ download_sim_psr.add_argument('install_path', help='path to install the simulator')
48
+ download_sim_psr.add_argument('--target-version', default='', help='specify a version other than the most recent to download')
49
+ download_sim_psr.add_argument(
50
+ '--copy-saved-from',
51
+ default='',
52
+ help='path to an existing installation from which to copy saved information')
53
+ download_sim_psr.add_argument(
54
+ '--update-existing',
55
+ action='store_true',
56
+ help='indicates that the user understands that they are updating an existing installation')
57
+ download_sim_psr.set_defaults(func=self.download_simulator)
58
+
59
+ def register_license(self, args):
60
+ print('Trying to Register License File: {}'.format(args.license_path))
61
+
62
+ # Load and validate provided license file
63
+ license_ok, license_status, license = validate_license_file(args.license_path)
64
+ if not license_ok:
65
+ print('Error Reading License File: {}'.format(license_status))
66
+ return
67
+
68
+ # Cache license info in config
69
+ license_name = os.path.basename(args.license_path)
70
+ self.cfg['license'] = {
71
+ 'filename': os.path.basename(license_name),
72
+ 'data': license
73
+ }
74
+ save_cfg('avrs', self.cfg)
75
+ print('License Registration Success: {}'.format(self.cfg['license']['filename']))
76
+
77
+ # Try to exchange license info for download keys
78
+ print('Retrieving Launcher Download Keys...')
79
+ (dl_keys_ok, dl_keys_status, self.cfg) = try_get_launcher_download_keys(self.cfg)
80
+ if not dl_keys_ok:
81
+ print('Error Getting Launcher Download Keys: {}'.format(dl_keys_status))
82
+ return
83
+ save_cfg('avrs', self.cfg)
84
+ print('Download Key Retrieval Success')
85
+
86
+ def license_status(self, args):
87
+ status = get_launcher_license_status_string(self.cfg)
88
+ print('Launcher License Status: {}'.format(status))
89
+
90
+ def get_available_sim_versions(self, args):
91
+ pass
92
+
93
+ # query something to find out what versions of the sim we can download
94
+
95
+ def get_variants(self, args):
96
+ if 'license' not in self.cfg:
97
+ print('no license has been registered\n')
98
+ return
99
+
100
+ (dl_keys_ok, dl_keys_status, self.cfg) = try_get_launcher_download_keys(self.cfg)
101
+ if not dl_keys_ok:
102
+ print('Error Getting Launcher Download Keys: {}'.format(dl_keys_status))
103
+ return
104
+ save_cfg('avrs', self.cfg)
105
+ print('{}'.format(self.cfg['variants']))
106
+
107
+
108
+ def get_latest_sim_version(self, args):
109
+ self.logger.info('getting latest sim version')
110
+ # Validate status of download keys
111
+ dl_keys_ok, dl_keys_status = has_launcher_download_keys(self.cfg)
112
+ if not dl_keys_ok:
113
+ print('Launcher Download Keys Error: {}'.format(dl_keys_status))
114
+ return
115
+
116
+ # Check if variant is valid (for backwards compatibility, we still proceed even if invalid)
117
+ variant_is_invalid = False
118
+ if args.variant not in ["staged", "feature"]:
119
+ # Ensure variants are loaded
120
+ if 'variants' not in self.cfg:
121
+ (dl_keys_ok, dl_keys_status, self.cfg) = try_get_launcher_download_keys(self.cfg)
122
+ if not dl_keys_ok:
123
+ # If we can't get variants, proceed anyway for backwards compatibility
124
+ pass
125
+ if 'variants' in self.cfg and args.variant not in self.cfg['variants']:
126
+ variant_is_invalid = True
127
+ # Write warning to stderr so it doesn't interfere with scripts parsing stdout
128
+ sys.stderr.write('Warning: "{}" is not a known variant. Available variants: {}\n'.format(
129
+ args.variant, self.cfg['variants']))
130
+
131
+ variant_to_query = "autoverse" if args.variant in ["staged", "feature"] else args.variant
132
+
133
+ # Get latest build info from API
134
+ build_info_ok, build_info_status, latest_version, staged_version, build_info = get_launcher_build_info(
135
+ self.cfg, variant_to_query)
136
+ if not build_info_ok:
137
+ print('Error Getting Latest Version Info: {}'.format(build_info_status))
138
+ return
139
+
140
+ if args.variant == "staged":
141
+ print('{}'.format(staged_version))
142
+ elif args.variant == "feature":
143
+ feature_info = build_info.get('feature', {})
144
+ if isinstance(feature_info, str):
145
+ try:
146
+ feature_info = json.loads(feature_info)
147
+ except json.JSONDecodeError:
148
+ pass
149
+ print('{}'.format(json.dumps(feature_info)))
150
+ else:
151
+ print('{}'.format(latest_version))
152
+ if variant_is_invalid:
153
+ # Explain that the output is the default/prod variant version, not the requested variant
154
+ sys.stderr.write('Note: The version shown above is the production version of the default variant, not "{}"\n'.format(
155
+ args.variant))
156
+
157
+ def download_simulator(self, args):
158
+
159
+ # update variants before each download. also updates download keys
160
+ print('Updating Variants...')
161
+ self.get_variants(args)
162
+
163
+ if args.variant not in self.cfg['variants']:
164
+ print('\"{}\" is not a known variant, try one of: {}'.format(
165
+ args.variant, self.cfg['variants']))
166
+ return
167
+
168
+ file_path = args.install_path
169
+ if not os.path.exists(file_path):
170
+ print('Cannot Install Simulator at: {} (Invalid Path, Specify a Directory)'.format(file_path))
171
+ return
172
+
173
+ # Validate status of download keys
174
+ dl_keys_ok, dl_keys_status = has_launcher_download_keys(self.cfg)
175
+ if not dl_keys_ok:
176
+ print('Launcher Download Keys Error: {}'.format(dl_keys_status))
177
+ return
178
+
179
+ # Get latest build info from API
180
+ build_info_ok, build_info_status, latest_version, staged_version, _build_info = get_launcher_build_info(
181
+ self.cfg, args.variant)
182
+ if not build_info_ok:
183
+ print('Error Getting Latest Version Info: {}'.format(build_info_status))
184
+ return
185
+
186
+ # If we want to copy saved info from another installation
187
+ copy_saved = False
188
+ copy_saved_cache = ''
189
+ if args.copy_saved_from != '':
190
+ if not is_installed_sim(args.copy_saved_from):
191
+ print('The Path: {} Is not an Existing Installation. Cannot Copy Saved'.format(args.copy_saved_from))
192
+ return
193
+ else:
194
+ copy_saved = True
195
+
196
+ if copy_saved:
197
+ print('Caching Saved from Existing Installation at: {}'.format(args.copy_saved_from))
198
+ copy_from_saved_dir = os.path.join(args.copy_saved_from, 'Linux', 'Autoverse', 'Saved')
199
+ copy_saved_cache = os.path.join(args.install_path, 'autoverse-saved-cache')
200
+ shutil.copytree(copy_from_saved_dir, copy_saved_cache)
201
+
202
+ target_version = latest_version
203
+ if args.target_version == 'staged':
204
+ target_version = staged_version
205
+ print('attempting to download staged version: {}'.format(target_version))
206
+ elif args.target_version != '':
207
+ target_version = args.target_version
208
+ print('attempting to download specified version: {}'.format(target_version))
209
+
210
+ package_zip_name = 'autoverse-linux-{}.zip'.format(target_version)
211
+ package_name = 'autoverse-linux'
212
+ if target_version == 'feature':
213
+ args.variant = 'feature'
214
+ bucket_path = '{}/{}'.format(args.variant, package_zip_name)
215
+ dl_path = os.path.join(args.install_path, package_zip_name)
216
+ unzip_path = os.path.join(args.install_path, package_name)
217
+
218
+ if is_installed_sim(unzip_path) and not args.update_existing:
219
+ print('''
220
+ Downloading at {} will update an existing installation and override its saved information.
221
+ make sure you have backups of any configuration or use the --copy-saved-from option.
222
+ If you are sure, re-run with the --update-existing flag
223
+ '''.format(unzip_path))
224
+ return
225
+
226
+ if not is_installed_sim(unzip_path):
227
+ if not 'installs' in self.cfg:
228
+ self.cfg['installs'] = []
229
+ self.cfg['installs'].append(unzip_path)
230
+ save_cfg('avrs', self.cfg)
231
+
232
+ print('Downloading {} to {}. This May Take Several Minutes Depending on Connection Speed'.format(bucket_path, dl_path))
233
+ download_simulator_archive(self.cfg, bucket_path, dl_path)
234
+
235
+ print('Extracting {} to {}'.format(dl_path, unzip_path))
236
+ shutil.unpack_archive(dl_path, unzip_path)
237
+
238
+ if copy_saved:
239
+ print('Migrating Saved Files')
240
+ shutil.copytree(copy_saved_cache, os.path.join(unzip_path, 'Linux', 'Autoverse', 'Saved'), dirs_exist_ok=True)
241
+ shutil.rmtree(copy_saved_cache)
242
+
243
+ # Install license if missing
244
+ if not copy_saved:
245
+ print('Installing Registered License: {}'.format(self.cfg['license']['filename']))
246
+ license_install_path = os.path.join(unzip_path, 'Linux', 'Autoverse', 'Saved', self.cfg['license']['filename'])
247
+ with open(license_install_path, 'w', encoding='utf-8') as f:
248
+ json.dump(self.cfg['license']['data'], f, ensure_ascii=False, indent=4)
249
+
250
+ # Make Autovers.sh executable
251
+ autoverse_exe = os.path.join(unzip_path, 'Linux', 'Autoverse.sh')
252
+ st = os.stat(autoverse_exe)
253
+ os.chmod(autoverse_exe, st.st_mode | stat.S_IEXEC)
254
+
255
+ print('Cleaning up')
256
+ os.remove(dl_path)