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.
- autoverse_cli-0.28.1.dist-info/METADATA +91 -0
- autoverse_cli-0.28.1.dist-info/RECORD +51 -0
- autoverse_cli-0.28.1.dist-info/WHEEL +5 -0
- autoverse_cli-0.28.1.dist-info/entry_points.txt +2 -0
- autoverse_cli-0.28.1.dist-info/licenses/LICENSE +33 -0
- autoverse_cli-0.28.1.dist-info/top_level.txt +1 -0
- avrs/__init__.py +0 -0
- avrs/app_version.py +24 -0
- avrs/argparse_help.py +30 -0
- avrs/avrs.py +183 -0
- avrs/can_tool.py +192 -0
- avrs/can_tool_util.py +190 -0
- avrs/cfg.py +78 -0
- avrs/launcher.py +256 -0
- avrs/launcher_util.py +203 -0
- avrs/race_cloud.py +506 -0
- avrs/race_cloud_bridge_can.py +100 -0
- avrs/race_cloud_cfg_util.py +310 -0
- avrs/race_cloud_fwd_api.py +49 -0
- avrs/race_cloud_util.py +384 -0
- avrs/requests/change_camera.py +24 -0
- avrs/requests/code_booz.py +69 -0
- avrs/requests/demo.py +19 -0
- avrs/requests/dump_sim_config.py +14 -0
- avrs/requests/environment.py +46 -0
- avrs/requests/fault_injection.py +186 -0
- avrs/requests/get_object_config.py +18 -0
- avrs/requests/get_web_viz_meta.py +11 -0
- avrs/requests/leaderboard.py +74 -0
- avrs/requests/list_sim_objects.py +26 -0
- avrs/requests/log_path.py +28 -0
- avrs/requests/misc.py +70 -0
- avrs/requests/move_to_landmark.py +16 -0
- avrs/requests/npc.py +289 -0
- avrs/requests/race_control.py +48 -0
- avrs/requests/request.py +61 -0
- avrs/requests/reset_to_track.py +21 -0
- avrs/requests/rest_request.py +45 -0
- avrs/requests/restart.py +12 -0
- avrs/requests/scenario_control.py +43 -0
- avrs/requests/spawn_object.py +44 -0
- avrs/requests/teleport.py +40 -0
- avrs/requests/toggle_hud.py +11 -0
- avrs/requests/vd.py +64 -0
- avrs/requests/vehicle_input.py +21 -0
- avrs/requests/vehicle_replay.py +454 -0
- avrs/shell_completion.py +121 -0
- avrs/simconfig.py +75 -0
- avrs/simconfig_util.py +170 -0
- avrs/tests.py +9 -0
- avrs/util.py +13 -0
avrs/race_cloud.py
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import base64
|
|
3
|
+
import logging
|
|
4
|
+
import time
|
|
5
|
+
from avrs.cfg import *
|
|
6
|
+
from avrs.race_cloud_util import *
|
|
7
|
+
from avrs.race_cloud_cfg_util import *
|
|
8
|
+
from avrs.race_cloud_fwd_api import *
|
|
9
|
+
from avrs.race_cloud_bridge_can import *
|
|
10
|
+
from avrs.util import *
|
|
11
|
+
from avrs.requests.request import AvrsApiRequest
|
|
12
|
+
from argparse import RawTextHelpFormatter
|
|
13
|
+
|
|
14
|
+
class AvrsRaceCloud(AvrsApiRequest):
|
|
15
|
+
def __init__(self, parser, cfg):
|
|
16
|
+
self.cfg = cfg
|
|
17
|
+
race_cloud_parser = parser.add_parser(
|
|
18
|
+
'race-cloud',
|
|
19
|
+
help='cloud racing\n\n')
|
|
20
|
+
|
|
21
|
+
sps = race_cloud_parser.add_subparsers(required=True, help='race-cloud options')
|
|
22
|
+
|
|
23
|
+
connect_parser = sps.add_parser(
|
|
24
|
+
'connect',
|
|
25
|
+
help='connect to an instance for cloud racing')
|
|
26
|
+
connect_parser.add_argument(
|
|
27
|
+
'sim_index',
|
|
28
|
+
type = int,
|
|
29
|
+
choices = [0, 1, 2, 3, 4, 5],
|
|
30
|
+
help='the index of the simulator instance to connect to')
|
|
31
|
+
connect_parser.add_argument(
|
|
32
|
+
'team_name',
|
|
33
|
+
help='the name of the team to race under')
|
|
34
|
+
connect_parser.add_argument(
|
|
35
|
+
'vehicle_config',
|
|
36
|
+
help='the path the vehicle configuration file to use when racing')
|
|
37
|
+
connect_parser.add_argument(
|
|
38
|
+
'--instance-id',
|
|
39
|
+
default = '',
|
|
40
|
+
help='can be used directly instead of instance name if needed')
|
|
41
|
+
connect_parser.add_argument(
|
|
42
|
+
'--no-restart-sim',
|
|
43
|
+
action='store_true',
|
|
44
|
+
help='if set, the sim instance will not be restarted after connection')
|
|
45
|
+
connect_parser.add_argument(
|
|
46
|
+
'--no-check-cans',
|
|
47
|
+
action='store_true',
|
|
48
|
+
help='if set, vcan interfaces will not be checked for validity')
|
|
49
|
+
connect_parser.add_argument(
|
|
50
|
+
'--print-debug',
|
|
51
|
+
action='store_true',
|
|
52
|
+
help='if set, debug information will be printed')
|
|
53
|
+
connect_parser.set_defaults(func=self.race_connect)
|
|
54
|
+
|
|
55
|
+
rx_connect_parser = sps.add_parser(
|
|
56
|
+
'rx-connection',
|
|
57
|
+
help='execute steps to receive a connection (only relevant to sim-side)')
|
|
58
|
+
rx_connect_parser.add_argument(
|
|
59
|
+
'team_name',
|
|
60
|
+
help='the name of the connecting team')
|
|
61
|
+
rx_connect_parser.add_argument(
|
|
62
|
+
'ip',
|
|
63
|
+
help='the ip address of the incoming connection')
|
|
64
|
+
rx_connect_parser.add_argument(
|
|
65
|
+
'--restart',
|
|
66
|
+
action='store_true',
|
|
67
|
+
help='should the sim program be restarted after this connection')
|
|
68
|
+
rx_connect_parser.add_argument(
|
|
69
|
+
'cfg_data',
|
|
70
|
+
help='the incoming vehicle configuration data')
|
|
71
|
+
rx_connect_parser.set_defaults(func=self.rx_connect)
|
|
72
|
+
|
|
73
|
+
sim_ctrl_parser = sps.add_parser(
|
|
74
|
+
'sim-ctrl',
|
|
75
|
+
help='control the sim program (start, stop, restart, reset)')
|
|
76
|
+
sim_ctrl_parser.add_argument(
|
|
77
|
+
'sim_index',
|
|
78
|
+
type=int,
|
|
79
|
+
choices=[0, 1, 2, 3, 4, 5],
|
|
80
|
+
help='the index of the simulator instance to apply the action')
|
|
81
|
+
sim_ctrl_parser.add_argument(
|
|
82
|
+
'action',
|
|
83
|
+
choices=['start', 'stop', 'restart', 'reset-connection', 'get-log'],
|
|
84
|
+
help='what action to apply to the simulator program')
|
|
85
|
+
sim_ctrl_parser.add_argument(
|
|
86
|
+
'--local',
|
|
87
|
+
action='store_true',
|
|
88
|
+
help='if set, this command will run on this system locally (not for use from dev instances)')
|
|
89
|
+
sim_ctrl_parser.add_argument(
|
|
90
|
+
'--instance-id',
|
|
91
|
+
default = '',
|
|
92
|
+
help='can be used directly instead of sim index if needed')
|
|
93
|
+
sim_ctrl_parser.add_argument(
|
|
94
|
+
'--clear-autospawns',
|
|
95
|
+
action='store_true',
|
|
96
|
+
help='can be used with the reset-connection action to clear sim autospawn config')
|
|
97
|
+
sim_ctrl_parser.set_defaults(func=self.sim_ctrl)
|
|
98
|
+
|
|
99
|
+
reset_qos_parser = sps.add_parser(
|
|
100
|
+
'reset-qos',
|
|
101
|
+
help='reset the qos file on this system')
|
|
102
|
+
reset_qos_parser.set_defaults(func=self.reset_qos)
|
|
103
|
+
|
|
104
|
+
enable_peer_qos_parser = sps.add_parser(
|
|
105
|
+
'enable-peer-qos',
|
|
106
|
+
help='enable a peer in this systems qos file')
|
|
107
|
+
enable_peer_qos_parser.add_argument(
|
|
108
|
+
'peer_id',
|
|
109
|
+
type=int,
|
|
110
|
+
choices=[0, 1, 2, 3, 4, 5],
|
|
111
|
+
help='the id of the peer to enable')
|
|
112
|
+
enable_peer_qos_parser.add_argument(
|
|
113
|
+
'ip',
|
|
114
|
+
help='the ip address to add to the qos file')
|
|
115
|
+
enable_peer_qos_parser.set_defaults(func=self.enable_peer_qos)
|
|
116
|
+
|
|
117
|
+
disable_peer_qos_parser = sps.add_parser(
|
|
118
|
+
'disable-peer-qos',
|
|
119
|
+
help='disable a peer in this systems qos file')
|
|
120
|
+
disable_peer_qos_parser.add_argument(
|
|
121
|
+
'peer_id',
|
|
122
|
+
type=int,
|
|
123
|
+
choices=[0, 1, 2, 3, 4, 5],
|
|
124
|
+
help='the id of the peer to enable')
|
|
125
|
+
disable_peer_qos_parser.set_defaults(func=self.disable_peer_qos)
|
|
126
|
+
|
|
127
|
+
fwd_server_parser = sps.add_parser(
|
|
128
|
+
'fwd-api',
|
|
129
|
+
help='forwards incoming external api requests to the simulator api')
|
|
130
|
+
fwd_server_parser.add_argument(
|
|
131
|
+
'mode',
|
|
132
|
+
choices=['bg', 'fg'],
|
|
133
|
+
help='whether to run in forground or background')
|
|
134
|
+
fwd_server_parser.add_argument(
|
|
135
|
+
'source_port',
|
|
136
|
+
type=int,
|
|
137
|
+
help='the external port to listen to external api requests on')
|
|
138
|
+
fwd_server_parser.add_argument(
|
|
139
|
+
'target_port',
|
|
140
|
+
type=int,
|
|
141
|
+
help='the local port to forward api requests too')
|
|
142
|
+
fwd_server_parser.set_defaults(func=self.fwd_api)
|
|
143
|
+
|
|
144
|
+
bridge_can_parser = sps.add_parser(
|
|
145
|
+
'bridge-can',
|
|
146
|
+
help='bridges a local vcan with a remote vcan over udp')
|
|
147
|
+
|
|
148
|
+
bridge_can_parser.add_argument(
|
|
149
|
+
'mode',
|
|
150
|
+
choices=['bg', 'fg'],
|
|
151
|
+
help='whether to run in forground or background')
|
|
152
|
+
|
|
153
|
+
bridge_can_parser.add_argument(
|
|
154
|
+
'vcan_name')
|
|
155
|
+
|
|
156
|
+
bridge_can_parser.add_argument(
|
|
157
|
+
'peer_ip')
|
|
158
|
+
|
|
159
|
+
bridge_can_parser.add_argument(
|
|
160
|
+
'peer_port',
|
|
161
|
+
type=int)
|
|
162
|
+
|
|
163
|
+
bridge_can_parser.add_argument(
|
|
164
|
+
'local_port',
|
|
165
|
+
type=int)
|
|
166
|
+
|
|
167
|
+
bridge_can_parser.set_defaults(func=self.bridge_can)
|
|
168
|
+
|
|
169
|
+
def race_connect(self, args):
|
|
170
|
+
logger = logging.getLogger('avrs')
|
|
171
|
+
|
|
172
|
+
# make api call to begin connection
|
|
173
|
+
our_ip = get_local_instance_ip()
|
|
174
|
+
|
|
175
|
+
if our_ip == '127.0.0.1':
|
|
176
|
+
print('this machines IP was returned as localhost. was this run on the cloud instance?')
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
logger.info('starting race-cloud connect for team {} to instance {}'.format(
|
|
180
|
+
args.team_name, args.sim_index))
|
|
181
|
+
print('connecting to race with team name: {}'.format(args.team_name))
|
|
182
|
+
|
|
183
|
+
if args.no_restart_sim:
|
|
184
|
+
print('the simulator will NOT be restarted automatically after this connection')
|
|
185
|
+
else:
|
|
186
|
+
print('the simulator WILL be restarted automatically after this connection')
|
|
187
|
+
|
|
188
|
+
# reset local connection first
|
|
189
|
+
logger.info('resetting local connection state prior to connection')
|
|
190
|
+
reset_race_cloud_connection()
|
|
191
|
+
|
|
192
|
+
# validate / load vehicle config
|
|
193
|
+
vcfg_ok, vcfg_data, bsu_vcan, kistler_vcan, badenia_vcan = prepare_vehicle_cfg(args.vehicle_config)
|
|
194
|
+
|
|
195
|
+
if not vcfg_ok:
|
|
196
|
+
print('error reading config file: {}'.format(vcfg_data))
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
print('creating vcan interfaces found in config locally: {}, {}, {} (if they do not exist)'.format(
|
|
200
|
+
bsu_vcan, kistler_vcan, badenia_vcan))
|
|
201
|
+
|
|
202
|
+
setup_vcans(bsu_vcan, kistler_vcan, badenia_vcan)
|
|
203
|
+
|
|
204
|
+
vcan_ok = True
|
|
205
|
+
if not check_vcan_exists(bsu_vcan) and not args.no_check_cans:
|
|
206
|
+
print('bsu vcan {} does not appear to exist'.format(bsu_vcan))
|
|
207
|
+
vcan_ok = False
|
|
208
|
+
if not check_vcan_exists(kistler_vcan) and not args.no_check_cans:
|
|
209
|
+
print('kistler vcan {} does not appear to exist'.format(kistler_vcan))
|
|
210
|
+
vcan_ok = False
|
|
211
|
+
if not check_vcan_exists(badenia_vcan) and not args.no_check_cans:
|
|
212
|
+
print('badenia vcan {} does not appear to exist'.format(badenia_vcan))
|
|
213
|
+
vcan_ok = False
|
|
214
|
+
|
|
215
|
+
if not vcan_ok:
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
connection_request = {
|
|
219
|
+
'action': 'connect',
|
|
220
|
+
'sim_index': args.sim_index,
|
|
221
|
+
'ip_to_reserve': our_ip,
|
|
222
|
+
'team_name': args.team_name,
|
|
223
|
+
'sim_id_override': args.instance_id,
|
|
224
|
+
'ensure_instance_is_running': False,
|
|
225
|
+
'should_restart_sim_program': not args.no_restart_sim,
|
|
226
|
+
'config_data': vcfg_data
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
ok, response = call_race_cloud_api(connection_request)
|
|
230
|
+
if not ok:
|
|
231
|
+
print('connect api error: {}'.format(response))
|
|
232
|
+
|
|
233
|
+
ok, rbody, sim_ip = get_api_script_response(response)
|
|
234
|
+
if not ok:
|
|
235
|
+
print(rbody)
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
if args.print_debug:
|
|
239
|
+
print(rbody)
|
|
240
|
+
#print(sim_ip)
|
|
241
|
+
|
|
242
|
+
slot_info = {}
|
|
243
|
+
for k, v in rbody.items():
|
|
244
|
+
#print('out: {}'.format(v['stdout']))
|
|
245
|
+
#print('err: {}'.format(v['stderr']))
|
|
246
|
+
slot_info = json.loads(v['stdout'])
|
|
247
|
+
|
|
248
|
+
if not slot_info['ok']:
|
|
249
|
+
print('issue reserving slot: {}'.format(slot_info['msg']))
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
extra_msg = slot_info.get('extra_msg', '')
|
|
253
|
+
print(extra_msg)
|
|
254
|
+
|
|
255
|
+
sim_slot = slot_info['slot']
|
|
256
|
+
|
|
257
|
+
# enable first peer since this is on a client (only will ever have 1, the sim)
|
|
258
|
+
enable_peer_qos(sim_slot, sim_ip)
|
|
259
|
+
|
|
260
|
+
# will need to get port from received slot id to connect peer vcans
|
|
261
|
+
connect_peer_vcan(sim_slot, sim_ip, 0, bsu_vcan)
|
|
262
|
+
connect_peer_vcan(sim_slot, sim_ip, 1, kistler_vcan)
|
|
263
|
+
connect_peer_vcan(sim_slot, sim_ip, 2, badenia_vcan)
|
|
264
|
+
|
|
265
|
+
# configure the CLI to communicate with the cloud sim instance
|
|
266
|
+
self.cfg['sim_address'] = sim_ip
|
|
267
|
+
self.cfg['sim_api_port'] = 30333 # set this to the forwarding server running on sim instance
|
|
268
|
+
logger.info('setting sim addr to {} and port to {} for future api requests'.format(sim_ip, 30333))
|
|
269
|
+
save_cfg('avrs', self.cfg)
|
|
270
|
+
|
|
271
|
+
print('connection success with team name {} and slot id {}'.format(args.team_name, sim_slot))
|
|
272
|
+
|
|
273
|
+
# if sim slot is not 0, which means domain id is not 0, notify user
|
|
274
|
+
if sim_slot != 0:
|
|
275
|
+
print('your ROS2 interfaces will be available on ROS_DOMAIN_ID {}'.format(sim_slot))
|
|
276
|
+
print('you will need to set the ROS_DOMAIN_ID environment variable prior to echoing or running software')
|
|
277
|
+
print('(eg \"export ROS_DOMAIN_ID={}\")'.format(sim_slot))
|
|
278
|
+
|
|
279
|
+
# this should only run on sim instances (not dev instances)
|
|
280
|
+
def rx_connect(self, args):
|
|
281
|
+
logger = logging.getLogger('avrs')
|
|
282
|
+
|
|
283
|
+
extra_msg = ''
|
|
284
|
+
|
|
285
|
+
# check time-since last connection here. if > 10mins auto reset connection state
|
|
286
|
+
time_since_last_connection = self.get_and_update_time_since_last_connection()
|
|
287
|
+
reset_connection_timeout = 3600.0
|
|
288
|
+
if time_since_last_connection > 3600.0:
|
|
289
|
+
logger.info('time since last connection rx {} > {}. resetting connection'.format(
|
|
290
|
+
time_since_last_connection, reset_connection_timeout))
|
|
291
|
+
clear_autospawns()
|
|
292
|
+
reset_race_cloud_connection()
|
|
293
|
+
extra_msg += 'automatically resetting simulation instance connection state'
|
|
294
|
+
extra_msg += ' (last connection was > {} seconds ago)'.format(reset_connection_timeout)
|
|
295
|
+
else:
|
|
296
|
+
extra_msg += 'NOT resetting connection state. last connection was only {} seconds ago'.format(
|
|
297
|
+
time_since_last_connection)
|
|
298
|
+
|
|
299
|
+
logger.info('rx race cloud connection for team {} with ip {}'.format(args.team_name, args.ip))
|
|
300
|
+
ok, msg, slot = try_get_open_slot(args.team_name, args.ip)
|
|
301
|
+
|
|
302
|
+
bsu_vcan = get_auto_vcan_name(slot, 0)
|
|
303
|
+
kistler_vcan = get_auto_vcan_name(slot, 1)
|
|
304
|
+
badenia_vcan = get_auto_vcan_name(slot, 2)
|
|
305
|
+
|
|
306
|
+
# get CAN names from this
|
|
307
|
+
register_received_vehicle(
|
|
308
|
+
args.team_name, slot, args.cfg_data, bsu_vcan, kistler_vcan, badenia_vcan)
|
|
309
|
+
|
|
310
|
+
vcan_ok = True
|
|
311
|
+
vcan_msg = ''
|
|
312
|
+
if not check_vcan_exists(bsu_vcan):
|
|
313
|
+
vcan_msg = 'bsu vcan {} does not appear to exist'.format(bsu_vcan)
|
|
314
|
+
vcan_ok = False
|
|
315
|
+
if not check_vcan_exists(kistler_vcan):
|
|
316
|
+
vcan_msg = 'kistler vcan {} does not appear to exist'.format(kistler_vcan)
|
|
317
|
+
vcan_ok = False
|
|
318
|
+
if not check_vcan_exists(badenia_vcan):
|
|
319
|
+
vcan_msg = 'badenia vcan {} does not appear to exist'.format(badenia_vcan)
|
|
320
|
+
vcan_ok = False
|
|
321
|
+
|
|
322
|
+
if not vcan_ok:
|
|
323
|
+
print(json.dumps({
|
|
324
|
+
'ok': vcan_ok,
|
|
325
|
+
'msg': vcan_msg,
|
|
326
|
+
'slot': -1
|
|
327
|
+
}))
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
if ok:
|
|
331
|
+
logger.info('enabling qos for slot {} with ip {}'.format(slot, args.ip))
|
|
332
|
+
enable_peer_qos(slot, args.ip)
|
|
333
|
+
logger.info('connecting vcans for incoming connection')
|
|
334
|
+
out = connect_peer_vcan(slot, args.ip, 0)
|
|
335
|
+
logger.info('connected first vcan result: {} {}'.format(out.out, out.err))
|
|
336
|
+
out = connect_peer_vcan(slot, args.ip, 1)
|
|
337
|
+
logger.info('connected second vcan result: {} {}'.format(out.out, out.err))
|
|
338
|
+
out = connect_peer_vcan(slot, args.ip, 2)
|
|
339
|
+
logger.info('connected third vcan result: {} {}'.format(out.out, out.err))
|
|
340
|
+
|
|
341
|
+
# go ahead and restart forwarding script here
|
|
342
|
+
# stop it if its already running
|
|
343
|
+
stop_result = stop_fwd_api()
|
|
344
|
+
logger.info('stopped fwd-api: {}'.format(stop_result))
|
|
345
|
+
start_result = start_fwd_api(30333, 30313)
|
|
346
|
+
logger.info('started fwd-api: {}'.format(start_result))
|
|
347
|
+
|
|
348
|
+
if args.restart:
|
|
349
|
+
logger.info('restarting sim program for new connection')
|
|
350
|
+
logger.info('stopping sim program on sim instance')
|
|
351
|
+
bash_kill_process('Autoverse')
|
|
352
|
+
logger.info('starting sim program on sim instance')
|
|
353
|
+
exe_path = os.path.join(get_sim_exe_path())
|
|
354
|
+
start_exe(exe_path)
|
|
355
|
+
extra_msg += '\n' if len(extra_msg) > 0 else ''
|
|
356
|
+
extra_msg += 'automatically restarting sim program on sim instance'
|
|
357
|
+
else:
|
|
358
|
+
logger.info('--restart not specified, not restarting sim program for this connection')
|
|
359
|
+
|
|
360
|
+
response = {
|
|
361
|
+
'ok': ok,
|
|
362
|
+
'msg': msg,
|
|
363
|
+
'slot': slot,
|
|
364
|
+
'extra_msg': extra_msg
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
print(json.dumps(response)) # print this so that when called from the ssh lambda we can get the result
|
|
368
|
+
|
|
369
|
+
def sim_ctrl(self, args):
|
|
370
|
+
if args.local:
|
|
371
|
+
self.local_sim_ctrl(args)
|
|
372
|
+
else:
|
|
373
|
+
self.remote_sim_ctrl(args)
|
|
374
|
+
|
|
375
|
+
def local_sim_ctrl(self, args):
|
|
376
|
+
logger = logging.getLogger('avrs')
|
|
377
|
+
if args.action == 'reset-connection':
|
|
378
|
+
logger.info('resetting local race-cloud connection states')
|
|
379
|
+
print('resetting race cloud connection')
|
|
380
|
+
print('resetting qos file, removing CAN lock files, stopping all cannelloni instances')
|
|
381
|
+
reset_race_cloud_connection()
|
|
382
|
+
if args.clear_autospawns:
|
|
383
|
+
clear_autospawns()
|
|
384
|
+
if args.action == 'stop' or args.action == 'restart':
|
|
385
|
+
logger.info('stopping race-cloud simulator program')
|
|
386
|
+
print('stopping sim program on sim instance')
|
|
387
|
+
bash_kill_process('Autoverse')
|
|
388
|
+
if args.action == 'start' or args.action == 'restart':
|
|
389
|
+
logger.info('starting race-cloud simulator program')
|
|
390
|
+
#print('starting sim program on sim instance')
|
|
391
|
+
exe_path = os.path.join(get_sim_exe_path())
|
|
392
|
+
start_exe(exe_path)
|
|
393
|
+
if args.action == 'get-log':
|
|
394
|
+
logger.info('getting connection log')
|
|
395
|
+
log_path = os.path.join(get_cfg_dir('avrs'), 'avrs.log')
|
|
396
|
+
if os.path.isfile(log_path):
|
|
397
|
+
print('the get-log command has been deprecated')
|
|
398
|
+
#with open(log_path, 'r', encoding='utf-8') as f:
|
|
399
|
+
#print('the get-log command has been deprecated')
|
|
400
|
+
#print(f.read())
|
|
401
|
+
else:
|
|
402
|
+
print('no log file found')
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def remote_sim_ctrl(self, args):
|
|
406
|
+
logger = logging.getLogger('avrs')
|
|
407
|
+
|
|
408
|
+
# go ahead and reset local connection as well
|
|
409
|
+
if args.action == 'reset-connection':
|
|
410
|
+
reset_race_cloud_connection()
|
|
411
|
+
|
|
412
|
+
our_ip = get_local_instance_ip()
|
|
413
|
+
|
|
414
|
+
reset_request = {
|
|
415
|
+
'action': args.action,
|
|
416
|
+
'sim_index': args.sim_index,
|
|
417
|
+
'ip_to_reserve': our_ip,
|
|
418
|
+
'sim_id_override': args.instance_id,
|
|
419
|
+
'ensure_instance_is_running': False
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
logger.info('sending race-clolud sim-ctrl request: {}'.format(reset_request))
|
|
423
|
+
|
|
424
|
+
ok, response = call_race_cloud_api(reset_request)
|
|
425
|
+
#print(response)
|
|
426
|
+
if not ok:
|
|
427
|
+
print(response)
|
|
428
|
+
return
|
|
429
|
+
|
|
430
|
+
ok, rbody, sim_ip = get_api_script_response(response)
|
|
431
|
+
if not ok:
|
|
432
|
+
print(rbody)
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
for k, v in rbody.items():
|
|
436
|
+
print(v['stdout'])
|
|
437
|
+
|
|
438
|
+
def reset_qos(self, args):
|
|
439
|
+
print('resetting qos file')
|
|
440
|
+
reset_rmw_qos()
|
|
441
|
+
|
|
442
|
+
def enable_peer_qos(self, args):
|
|
443
|
+
print('enabling peer qos for id {} using ip address {}'.format(
|
|
444
|
+
args.peer_id, args.ip))
|
|
445
|
+
out = enable_peer_qos(args.peer_id, args.ip)
|
|
446
|
+
print('{} {}'.format(out.out, out.err))
|
|
447
|
+
|
|
448
|
+
def disable_peer_qos(self, args):
|
|
449
|
+
print('disabling peer qos for id {}'.format(args.peer_id))
|
|
450
|
+
out = disable_peer_qos(args.peer_id)
|
|
451
|
+
print('{} {}'.format(out.out, out.err))
|
|
452
|
+
|
|
453
|
+
def fwd_api(self, args):
|
|
454
|
+
# start a server that listens for http on some port and forwards
|
|
455
|
+
# to the simulator on 30313
|
|
456
|
+
logger = logging.getLogger('avrs')
|
|
457
|
+
|
|
458
|
+
if args.mode == 'bg':
|
|
459
|
+
# call the cli so that it starts the forwarding server in the background
|
|
460
|
+
# stop it if its already running
|
|
461
|
+
try:
|
|
462
|
+
stop_result = stop_fwd_api()
|
|
463
|
+
logger.info('stopped fwd-api: {}'.format(stop_result))
|
|
464
|
+
start_result = start_fwd_api(args.source_port, args.target_port)
|
|
465
|
+
logger.info('started fwd-api: {}'.format(start_result))
|
|
466
|
+
except Exception as e:
|
|
467
|
+
logger.error('failed to restart fwd-api: {}'.format(e))
|
|
468
|
+
|
|
469
|
+
else:
|
|
470
|
+
logger.info('starting fwd-api in forground')
|
|
471
|
+
handler = ApiForwardHandler(args.target_port)
|
|
472
|
+
server = HTTPServer(('0.0.0.0', args.source_port), handler)
|
|
473
|
+
server.serve_forever()
|
|
474
|
+
|
|
475
|
+
def test_reset_timeout(self, args):
|
|
476
|
+
print('testing reset timeout')
|
|
477
|
+
|
|
478
|
+
elapsed = self.get_and_update_time_since_last_connection()
|
|
479
|
+
print('elasped: {}'.format(elapsed))
|
|
480
|
+
|
|
481
|
+
def get_and_update_time_since_last_connection(self):
|
|
482
|
+
t = time.time()
|
|
483
|
+
# default to zero if not exist to force timeout
|
|
484
|
+
last_time = self.cfg.get('race-cloud-last-connect-time', 0)
|
|
485
|
+
elapsed = t - last_time
|
|
486
|
+
self.cfg['race-cloud-last-connect-time'] = t
|
|
487
|
+
save_cfg('avrs', self.cfg)
|
|
488
|
+
return elapsed
|
|
489
|
+
|
|
490
|
+
def bridge_can(self, args):
|
|
491
|
+
logger = logging.getLogger('avrs')
|
|
492
|
+
|
|
493
|
+
if args.mode == 'bg':
|
|
494
|
+
# call the cli so that it starts the forwarding server in the background
|
|
495
|
+
# stop it if its already running
|
|
496
|
+
try:
|
|
497
|
+
stop_result = stop_can_brdige()
|
|
498
|
+
logger.info('stopped can bridge: {}'.format(stop_result))
|
|
499
|
+
start_result = start_can_bridge(args)
|
|
500
|
+
logger.info('started can bridge: {}'.format(start_result))
|
|
501
|
+
except Exception as e:
|
|
502
|
+
logger.error('failed to restart can bridge: {}'.format(e))
|
|
503
|
+
|
|
504
|
+
else:
|
|
505
|
+
logger.info('starting can bridge in forground')
|
|
506
|
+
can_bridge_loop(args)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import can
|
|
2
|
+
import argparse
|
|
3
|
+
import socket
|
|
4
|
+
import struct
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
BUFFER_SIZE = 512
|
|
8
|
+
|
|
9
|
+
CAN_FRAME_FORMATS = [
|
|
10
|
+
'=H', # 0 bytes should never happen
|
|
11
|
+
'=HB',
|
|
12
|
+
'=HBB',
|
|
13
|
+
'=HBBB',
|
|
14
|
+
'=HBBBB',
|
|
15
|
+
'=HBBBBB',
|
|
16
|
+
'=HBBBBBB',
|
|
17
|
+
'=HBBBBBBB',
|
|
18
|
+
'=HBBBBBBBB'
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
class UdpTxCanListener(can.Listener):
|
|
22
|
+
def __init__(self, target_port, target_ip):
|
|
23
|
+
self.target_port = target_port
|
|
24
|
+
self.target_ip = target_ip
|
|
25
|
+
self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
26
|
+
|
|
27
|
+
def on_message_received(self, msg):
|
|
28
|
+
#print('l got m {}'.format(msg))
|
|
29
|
+
|
|
30
|
+
# we are getting data that is not 8 bytes (dlc probably not 8) so
|
|
31
|
+
# we need to zero-bad or something
|
|
32
|
+
data = struct.pack(CAN_FRAME_FORMATS[len(msg.data)], msg.arbitration_id, *msg.data)
|
|
33
|
+
self.s.sendto(data, (self.target_ip, self.target_port))
|
|
34
|
+
|
|
35
|
+
def can_bridge_loop(args):
|
|
36
|
+
logger = logging.getLogger('avrs')
|
|
37
|
+
logger.info('starting can bridge tx on {} to {}:{} (listen on {})'.format(
|
|
38
|
+
args.vcan_name, args.peer_ip, args.peer_port, args.local_port))
|
|
39
|
+
try:
|
|
40
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
41
|
+
#s.settimeout(0.1)
|
|
42
|
+
s.bind(('', args.local_port))
|
|
43
|
+
with can.interface.Bus(args.vcan_name, interface='socketcan') as bus:
|
|
44
|
+
l = UdpTxCanListener(args.peer_port, args.peer_ip)
|
|
45
|
+
can.Notifier(bus, [l])
|
|
46
|
+
while True:
|
|
47
|
+
try:
|
|
48
|
+
data, server = s.recvfrom(BUFFER_SIZE)
|
|
49
|
+
|
|
50
|
+
l = len(data)
|
|
51
|
+
|
|
52
|
+
# ensure data is expected size
|
|
53
|
+
if l > 10 or l < 2:
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
#print('got udp data {}'.format(data))
|
|
57
|
+
up = struct.unpack(CAN_FRAME_FORMATS[l - 2], data)
|
|
58
|
+
|
|
59
|
+
tx_msg = can.Message(
|
|
60
|
+
arbitration_id=up[0],
|
|
61
|
+
data=up[1:],
|
|
62
|
+
is_extended_id=False,
|
|
63
|
+
dlc=l - 2) # len is rx size - 1 bc we add id to front
|
|
64
|
+
|
|
65
|
+
bus.send(tx_msg)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error('can bridge error: {}'.format(e))
|
|
68
|
+
break
|
|
69
|
+
except Exception as e:
|
|
70
|
+
logger.error('can bridge setup error: {}'.format(e))
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
if __name__ == '__main__':
|
|
74
|
+
|
|
75
|
+
parser = argparse.ArgumentParser(
|
|
76
|
+
prog='canbridge',
|
|
77
|
+
description='can bridge')
|
|
78
|
+
|
|
79
|
+
vcan_name_psr = parser.add_argument(
|
|
80
|
+
'vcan_name')
|
|
81
|
+
|
|
82
|
+
peer_ip_psr = parser.add_argument(
|
|
83
|
+
'peer_ip')
|
|
84
|
+
|
|
85
|
+
peer_port_psr = parser.add_argument(
|
|
86
|
+
'peer_port',
|
|
87
|
+
type=int)
|
|
88
|
+
|
|
89
|
+
peer_port_psr = parser.add_argument(
|
|
90
|
+
'local_port',
|
|
91
|
+
type=int)
|
|
92
|
+
|
|
93
|
+
version_psr = parser.add_argument(
|
|
94
|
+
'--peer_ip',
|
|
95
|
+
help='')
|
|
96
|
+
|
|
97
|
+
parser.set_defaults(func=bridge_loop)
|
|
98
|
+
|
|
99
|
+
args = parser.parse_args()
|
|
100
|
+
args.func(args)
|