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/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)