parent
							
								
									2291683728
								
							
						
					
					
						commit
						d0d63b7fb9
					
				
				 3 changed files with 23 additions and 612 deletions
			
			
		| @ -1,81 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| # type: ignore | ||||
| import sys | ||||
| import matplotlib.pyplot as plt | ||||
| import numpy as np | ||||
| import cereal.messaging as messaging | ||||
| import time | ||||
| 
 | ||||
| # tool to plot one or more signals live. Call ex: | ||||
| #./rqplot.py log.carState.vEgo log.carState.aEgo | ||||
| 
 | ||||
| # TODO: can this tool consume 10x less cpu? | ||||
| 
 | ||||
| def recursive_getattr(x, name): | ||||
|    l = name.split('.') | ||||
|    if len(l) == 1: | ||||
|      return getattr(x, name) | ||||
|    else: | ||||
|      return recursive_getattr(getattr(x, l[0]), ".".join(l[1:]) ) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|   poller = messaging.Poller() | ||||
| 
 | ||||
|   services = [] | ||||
|   fields = [] | ||||
|   subs = [] | ||||
|   values = [] | ||||
| 
 | ||||
|   plt.ion() | ||||
|   fig, ax = plt.subplots() | ||||
|   #fig = plt.figure(figsize=(10, 15)) | ||||
|   #ax = fig.add_subplot(111) | ||||
|   ax.grid(True) | ||||
|   fig.canvas.draw() | ||||
| 
 | ||||
|   subs_name = sys.argv[1:] | ||||
|   lines = [] | ||||
|   x, y = [], [] | ||||
|   LEN = 500 | ||||
| 
 | ||||
|   for i, sub in enumerate(subs_name): | ||||
|     sub_split = sub.split(".") | ||||
|     services.append(sub_split[0]) | ||||
|     fields.append(".".join(sub_split[1:])) | ||||
|     subs.append(messaging.sub_sock(sub_split[0], poller)) | ||||
| 
 | ||||
|     x.append(np.ones(LEN)*np.nan) | ||||
|     y.append(np.ones(LEN)*np.nan) | ||||
|     lines.append(ax.plot(x[i], y[i])[0]) | ||||
| 
 | ||||
|   for l in lines: | ||||
|     l.set_marker("*") | ||||
| 
 | ||||
|   cur_t = 0. | ||||
|   ax.legend(subs_name) | ||||
|   ax.set_xlabel('time [s]') | ||||
| 
 | ||||
|   while 1: | ||||
|     print(1./(time.time() - cur_t)) | ||||
|     cur_t = time.time() | ||||
|     for i, s in enumerate(subs): | ||||
|       msg = messaging.recv_sock(s) | ||||
|       #msg = messaging.recv_one_or_none(s) | ||||
|       if msg is not None: | ||||
|         x[i] = np.append(x[i], getattr(msg, 'logMonoTime') / 1e9) | ||||
|         x[i] = np.delete(x[i], 0) | ||||
|         y[i] = np.append(y[i], recursive_getattr(msg, subs_name[i])) | ||||
|         y[i] = np.delete(y[i], 0) | ||||
| 
 | ||||
|         lines[i].set_xdata(x[i]) | ||||
|         lines[i].set_ydata(y[i]) | ||||
| 
 | ||||
|     ax.relim() | ||||
|     ax.autoscale_view(True, scaley=True, scalex=True) | ||||
| 
 | ||||
|     fig.canvas.blit(ax.bbox) | ||||
|     fig.canvas.flush_events() | ||||
| 
 | ||||
|     # just a bit of wait to avoid 100% CPU usage | ||||
|     time.sleep(0.001) | ||||
| @ -1,500 +0,0 @@ | ||||
| #!/usr/bin/env python3 | ||||
| import argparse | ||||
| import os | ||||
| import sys | ||||
| import zmq | ||||
| import time | ||||
| import signal | ||||
| import multiprocessing | ||||
| from uuid import uuid4 | ||||
| from collections import namedtuple | ||||
| from collections import deque | ||||
| from datetime import datetime | ||||
| 
 | ||||
| from cereal import log as capnp_log | ||||
| from cereal.services import service_list | ||||
| from cereal.messaging import pub_sock, MultiplePublishersError | ||||
| from cereal.visionipc.visionipc_pyx import VisionIpcServer, VisionStreamType  # pylint: disable=no-name-in-module, import-error | ||||
| from common import realtime | ||||
| from common.transformations.camera import eon_f_frame_size, tici_f_frame_size | ||||
| 
 | ||||
| from tools.lib.kbhit import KBHit | ||||
| from tools.lib.logreader import MultiLogIterator | ||||
| from tools.lib.route import Route | ||||
| from tools.lib.framereader import rgb24toyuv420 | ||||
| from tools.lib.route_framereader import RouteFrameReader | ||||
| 
 | ||||
| # Commands. | ||||
| SetRoute = namedtuple("SetRoute", ("name", "start_time", "data_dir")) | ||||
| SeekAbsoluteTime = namedtuple("SeekAbsoluteTime", ("secs",)) | ||||
| SeekRelativeTime = namedtuple("SeekRelativeTime", ("secs",)) | ||||
| TogglePause = namedtuple("TogglePause", ()) | ||||
| StopAndQuit = namedtuple("StopAndQuit", ()) | ||||
| VIPC_RGB = "rgb" | ||||
| VIPC_YUV = "yuv" | ||||
| 
 | ||||
| 
 | ||||
| class UnloggerWorker(object): | ||||
|   def __init__(self): | ||||
|     self._frame_reader = None | ||||
|     self._cookie = None | ||||
|     self._readahead = deque() | ||||
| 
 | ||||
|   def run(self, commands_address, data_address, pub_types): | ||||
|     zmq.Context._instance = None | ||||
|     commands_socket = zmq.Context.instance().socket(zmq.PULL) | ||||
|     commands_socket.connect(commands_address) | ||||
| 
 | ||||
|     data_socket = zmq.Context.instance().socket(zmq.PUSH) | ||||
|     data_socket.connect(data_address) | ||||
| 
 | ||||
|     poller = zmq.Poller() | ||||
|     poller.register(commands_socket, zmq.POLLIN) | ||||
| 
 | ||||
|     # We can't publish frames without roadEncodeIdx, so add when it's missing. | ||||
|     if "roadCameraState" in pub_types: | ||||
|       pub_types["roadEncodeIdx"] = None | ||||
| 
 | ||||
|     # gc.set_debug(gc.DEBUG_LEAK | gc.DEBUG_OBJECTS | gc.DEBUG_STATS | gc.DEBUG_SAVEALL | | ||||
|     # gc.DEBUG_UNCOLLECTABLE) | ||||
| 
 | ||||
|     # TODO: WARNING pycapnp leaks memory all over the place after unlogger runs for a while, gc | ||||
|     # pauses become huge because there are so many tracked objects solution will be to switch to new | ||||
|     # cython capnp | ||||
|     try: | ||||
|       route = None | ||||
|       while True: | ||||
|         while poller.poll(0.) or route is None: | ||||
|           cookie, cmd = commands_socket.recv_pyobj() | ||||
|           route = self._process_commands(cmd, route, pub_types) | ||||
| 
 | ||||
|         # **** get message **** | ||||
|         self._read_logs(cookie, pub_types) | ||||
|         self._send_logs(data_socket) | ||||
|     finally: | ||||
|       if self._frame_reader is not None: | ||||
|         self._frame_reader.close() | ||||
|       data_socket.close() | ||||
|       commands_socket.close() | ||||
| 
 | ||||
|   def _read_logs(self, cookie, pub_types): | ||||
|     fullHEVC = capnp_log.EncodeIndex.Type.fullHEVC | ||||
|     lr = self._lr | ||||
|     while len(self._readahead) < 1000: | ||||
|       route_time = lr.tell() | ||||
|       msg = next(lr) | ||||
|       typ = msg.which() | ||||
|       if typ not in pub_types: | ||||
|         continue | ||||
| 
 | ||||
|       # **** special case certain message types **** | ||||
|       if typ == "roadEncodeIdx" and msg.roadEncodeIdx.type == fullHEVC: | ||||
|         # this assumes the roadEncodeIdx always comes before the frame | ||||
|         self._frame_id_lookup[ | ||||
|           msg.roadEncodeIdx.frameId] = msg.roadEncodeIdx.segmentNum, msg.roadEncodeIdx.segmentId | ||||
|         #print "encode", msg.roadEncodeIdx.frameId, len(self._readahead), route_time | ||||
|       self._readahead.appendleft((typ, msg, route_time, cookie)) | ||||
| 
 | ||||
|   def _send_logs(self, data_socket): | ||||
|     while len(self._readahead) > 500: | ||||
|       typ, msg, route_time, cookie = self._readahead.pop() | ||||
|       smsg = msg.as_builder() | ||||
| 
 | ||||
|       if typ == "roadCameraState": | ||||
|         frame_id = msg.roadCameraState.frameId | ||||
| 
 | ||||
|         # Frame exists, make sure we have a framereader. | ||||
|         # load the frame readers as needed | ||||
|         s1 = time.time() | ||||
|         try: | ||||
|           img = self._frame_reader.get(frame_id, pix_fmt="rgb24") | ||||
|         except Exception: | ||||
|           img = None | ||||
| 
 | ||||
|         fr_time = time.time() - s1 | ||||
|         if fr_time > 0.05: | ||||
|           print("FRAME(%d) LAG -- %.2f ms" % (frame_id, fr_time*1000.0)) | ||||
| 
 | ||||
|         if img is not None: | ||||
| 
 | ||||
|           extra = (smsg.roadCameraState.frameId, smsg.roadCameraState.timestampSof, smsg.roadCameraState.timestampEof) | ||||
| 
 | ||||
|           # send YUV frame | ||||
|           if os.getenv("YUV") is not None: | ||||
|             img_yuv = rgb24toyuv420(img) | ||||
|             data_socket.send_pyobj((cookie, VIPC_YUV, msg.logMonoTime, route_time, extra), flags=zmq.SNDMORE) | ||||
|             data_socket.send(img_yuv.flatten().tobytes(), copy=False) | ||||
| 
 | ||||
|           img = img[:, :, ::-1]  # Convert RGB to BGR, which is what the camera outputs | ||||
|           img = img.flatten() | ||||
|           bts = img.tobytes() | ||||
| 
 | ||||
|           smsg.roadCameraState.image = bts | ||||
| 
 | ||||
|           # send RGB frame | ||||
|           data_socket.send_pyobj((cookie, VIPC_RGB, msg.logMonoTime, route_time, extra), flags=zmq.SNDMORE) | ||||
|           data_socket.send(bts, copy=False) | ||||
| 
 | ||||
|       data_socket.send_pyobj((cookie, typ, msg.logMonoTime, route_time), flags=zmq.SNDMORE) | ||||
|       data_socket.send(smsg.to_bytes(), copy=False) | ||||
| 
 | ||||
|   def _process_commands(self, cmd, route, pub_types): | ||||
|     seek_to = None | ||||
|     if route is None or (isinstance(cmd, SetRoute) and route.name != cmd.name): | ||||
|       seek_to = cmd.start_time | ||||
|       route = Route(cmd.name, cmd.data_dir) | ||||
|       self._lr = MultiLogIterator(route.log_paths(), wraparound=True) | ||||
|       if self._frame_reader is not None: | ||||
|         self._frame_reader.close() | ||||
|       if "roadCameraState" in pub_types or "roadEncodeIdx" in pub_types: | ||||
|         # reset frames for a route | ||||
|         self._frame_id_lookup = {} | ||||
|         self._frame_reader = RouteFrameReader( | ||||
|           route.camera_paths(), None, self._frame_id_lookup, readahead=True) | ||||
| 
 | ||||
|     # always reset this on a seek | ||||
|     if isinstance(cmd, SeekRelativeTime): | ||||
|       seek_to = self._lr.tell() + cmd.secs | ||||
|     elif isinstance(cmd, SeekAbsoluteTime): | ||||
|       seek_to = cmd.secs | ||||
|     elif isinstance(cmd, StopAndQuit): | ||||
|       exit() | ||||
| 
 | ||||
|     if seek_to is not None: | ||||
|       print("seeking", seek_to) | ||||
|       if not self._lr.seek(seek_to): | ||||
|         print("Can't seek: time out of bounds") | ||||
|       else: | ||||
|         next(self._lr)   # ignore one | ||||
|     return route | ||||
| 
 | ||||
| def _get_address_send_func(address): | ||||
|   sock = pub_sock(address) | ||||
|   return sock.send | ||||
| 
 | ||||
| def _get_vipc_server(length): | ||||
|   sizes = {3 * w * h: (w, h) for (w, h) in [tici_f_frame_size, eon_f_frame_size]} # RGB | ||||
|   sizes.update({(3 * w * h) / 2: (w, h) for (w, h) in [tici_f_frame_size, eon_f_frame_size]}) # YUV | ||||
| 
 | ||||
|   w, h = sizes[length] | ||||
| 
 | ||||
|   vipc_server = VisionIpcServer("camerad") | ||||
|   vipc_server.create_buffers(VisionStreamType.VISION_STREAM_RGB_BACK, 4, True, w, h) | ||||
|   vipc_server.create_buffers(VisionStreamType.VISION_STREAM_YUV_BACK, 40, False, w, h) | ||||
|   vipc_server.start_listener() | ||||
|   return vipc_server | ||||
| 
 | ||||
| def unlogger_thread(command_address, forward_commands_address, data_address, run_realtime, | ||||
|                     address_mapping, publish_time_length, bind_early, no_loop, no_visionipc): | ||||
|   # Clear context to avoid problems with multiprocessing. | ||||
|   zmq.Context._instance = None | ||||
|   context = zmq.Context.instance() | ||||
| 
 | ||||
|   command_sock = context.socket(zmq.PULL) | ||||
|   command_sock.bind(command_address) | ||||
| 
 | ||||
|   forward_commands_socket = context.socket(zmq.PUSH) | ||||
|   forward_commands_socket.bind(forward_commands_address) | ||||
| 
 | ||||
|   data_socket = context.socket(zmq.PULL) | ||||
|   data_socket.bind(data_address) | ||||
| 
 | ||||
|   # Set readahead to a reasonable number. | ||||
|   data_socket.setsockopt(zmq.RCVHWM, 10000) | ||||
| 
 | ||||
|   poller = zmq.Poller() | ||||
|   poller.register(command_sock, zmq.POLLIN) | ||||
|   poller.register(data_socket, zmq.POLLIN) | ||||
| 
 | ||||
|   if bind_early: | ||||
|     send_funcs = { | ||||
|       typ: _get_address_send_func(address) | ||||
|       for typ, address in address_mapping.items() | ||||
|     } | ||||
| 
 | ||||
|     # Give subscribers a chance to connect. | ||||
|     time.sleep(0.1) | ||||
|   else: | ||||
|     send_funcs = {} | ||||
| 
 | ||||
|   start_time = float("inf") | ||||
|   printed_at = 0 | ||||
|   generation = 0 | ||||
|   paused = False | ||||
|   reset_time = True | ||||
|   prev_msg_time = None | ||||
|   vipc_server = None | ||||
| 
 | ||||
|   while True: | ||||
|     evts = dict(poller.poll()) | ||||
|     if command_sock in evts: | ||||
|       cmd = command_sock.recv_pyobj() | ||||
|       if isinstance(cmd, TogglePause): | ||||
|         paused = not paused | ||||
|         if paused: | ||||
|           poller.modify(data_socket, 0) | ||||
|         else: | ||||
|           poller.modify(data_socket, zmq.POLLIN) | ||||
|       else: | ||||
|         # Forward the command the the log data thread. | ||||
|         # TODO: Remove everything on data_socket. | ||||
|         generation += 1 | ||||
|         forward_commands_socket.send_pyobj((generation, cmd)) | ||||
|         if isinstance(cmd, StopAndQuit): | ||||
|           return | ||||
| 
 | ||||
|       reset_time = True | ||||
|     elif data_socket in evts: | ||||
|       msg_generation, typ, msg_time, route_time, *extra = data_socket.recv_pyobj(flags=zmq.RCVMORE) | ||||
|       msg_bytes = data_socket.recv() | ||||
|       if msg_generation < generation: | ||||
|         # Skip packets. | ||||
|         continue | ||||
| 
 | ||||
|       if no_loop and prev_msg_time is not None and prev_msg_time > msg_time + 1e9: | ||||
|         generation += 1 | ||||
|         forward_commands_socket.send_pyobj((generation, StopAndQuit())) | ||||
|         return | ||||
|       prev_msg_time = msg_time | ||||
| 
 | ||||
|       msg_time_seconds = msg_time * 1e-9 | ||||
|       if reset_time: | ||||
|         msg_start_time = msg_time_seconds | ||||
|         real_start_time = realtime.sec_since_boot() | ||||
|         start_time = min(start_time, msg_start_time) | ||||
|         reset_time = False | ||||
| 
 | ||||
|       if publish_time_length and msg_time_seconds - start_time > publish_time_length: | ||||
|         generation += 1 | ||||
|         forward_commands_socket.send_pyobj((generation, StopAndQuit())) | ||||
|         return | ||||
| 
 | ||||
|       # Print time. | ||||
|       if abs(printed_at - route_time) > 5.: | ||||
|         print("at", route_time) | ||||
|         printed_at = route_time | ||||
| 
 | ||||
|       if typ not in send_funcs and typ not in [VIPC_RGB, VIPC_YUV]: | ||||
|         if typ in address_mapping: | ||||
|           # Remove so we don't keep printing warnings. | ||||
|           address = address_mapping.pop(typ) | ||||
|           try: | ||||
|             print("binding", typ) | ||||
|             send_funcs[typ] = _get_address_send_func(address) | ||||
|           except Exception as e: | ||||
|             print("couldn't replay {}: {}".format(typ, e)) | ||||
|             continue | ||||
|         else: | ||||
|           # Skip messages that we are not registered to publish. | ||||
|           continue | ||||
| 
 | ||||
|       # Sleep as needed for real time playback. | ||||
|       if run_realtime: | ||||
|         msg_time_offset = msg_time_seconds - msg_start_time | ||||
|         real_time_offset = realtime.sec_since_boot() - real_start_time | ||||
|         lag = msg_time_offset - real_time_offset | ||||
|         if lag > 0 and lag < 30:  # a large jump is OK, likely due to an out of order segment | ||||
|           if lag > 1: | ||||
|             print("sleeping for", lag) | ||||
|           time.sleep(lag) | ||||
|         elif lag < -1: | ||||
|           # Relax the real time schedule when we slip far behind. | ||||
|           reset_time = True | ||||
| 
 | ||||
|       # Send message. | ||||
|       try: | ||||
|         if typ in [VIPC_RGB, VIPC_YUV]: | ||||
|           if not no_visionipc: | ||||
|             if vipc_server is None: | ||||
|               vipc_server = _get_vipc_server(len(msg_bytes)) | ||||
| 
 | ||||
|             i, sof, eof = extra[0] | ||||
|             stream = VisionStreamType.VISION_STREAM_RGB_BACK if typ == VIPC_RGB else VisionStreamType.VISION_STREAM_YUV_BACK | ||||
|             vipc_server.send(stream, msg_bytes, i, sof, eof) | ||||
|         else: | ||||
|           send_funcs[typ](msg_bytes) | ||||
|       except MultiplePublishersError: | ||||
|         del send_funcs[typ] | ||||
| 
 | ||||
| def timestamp_to_s(tss): | ||||
|   return time.mktime(datetime.strptime(tss, '%Y-%m-%d--%H-%M-%S').timetuple()) | ||||
| 
 | ||||
| def absolute_time_str(s, start_time): | ||||
|   try: | ||||
|     # first try if it's a float | ||||
|     return float(s) | ||||
|   except ValueError: | ||||
|     # now see if it's a timestamp | ||||
|     return timestamp_to_s(s) - start_time | ||||
| 
 | ||||
| def _get_address_mapping(args): | ||||
|   if args.min is not None: | ||||
|     services_to_mock = [ | ||||
|       'deviceState', 'can', 'pandaState', 'sensorEvents', 'gpsNMEA', 'roadCameraState', 'roadEncodeIdx', | ||||
|       'modelV2', 'liveLocation', | ||||
|     ] | ||||
|   elif args.enabled is not None: | ||||
|     services_to_mock = args.enabled | ||||
|   else: | ||||
|     services_to_mock = service_list.keys() | ||||
| 
 | ||||
|   address_mapping = {service_name: service_name for service_name in services_to_mock} | ||||
|   address_mapping.update(dict(args.address_mapping)) | ||||
| 
 | ||||
|   for k in args.disabled: | ||||
|     address_mapping.pop(k, None) | ||||
| 
 | ||||
|   non_services = set(address_mapping) - set(service_list) | ||||
|   if non_services: | ||||
|     print("WARNING: Unknown services {}".format(list(non_services))) | ||||
| 
 | ||||
|   return address_mapping | ||||
| 
 | ||||
| def keyboard_controller_thread(q, route_start_time): | ||||
|   print("keyboard waiting for input") | ||||
|   kb = KBHit() | ||||
|   while 1: | ||||
|     c = kb.getch() | ||||
|     if c == 'm':  # Move forward by 1m | ||||
|       q.send_pyobj(SeekRelativeTime(60)) | ||||
|     elif c == 'M':  # Move backward by 1m | ||||
|       q.send_pyobj(SeekRelativeTime(-60)) | ||||
|     elif c == 's':  # Move forward by 10s | ||||
|       q.send_pyobj(SeekRelativeTime(10)) | ||||
|     elif c == 'S':  # Move backward by 10s | ||||
|       q.send_pyobj(SeekRelativeTime(-10)) | ||||
|     elif c == 'G':  # Move backward by 10s | ||||
|       q.send_pyobj(SeekAbsoluteTime(0.)) | ||||
|     elif c == "\x20":  # Space bar. | ||||
|       q.send_pyobj(TogglePause()) | ||||
|     elif c == "\n": | ||||
|       try: | ||||
|         seek_time_input = input('time: ') | ||||
|         seek_time = absolute_time_str(seek_time_input, route_start_time) | ||||
| 
 | ||||
|         # If less than 60, assume segment number | ||||
|         if seek_time < 60: | ||||
|           seek_time *= 60 | ||||
| 
 | ||||
|         q.send_pyobj(SeekAbsoluteTime(seek_time)) | ||||
|       except Exception as e: | ||||
|         print("Time not understood: {}".format(e)) | ||||
| 
 | ||||
| def get_arg_parser(): | ||||
|   parser = argparse.ArgumentParser( | ||||
|     description="Mock openpilot components by publishing logged messages.", | ||||
|     formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
| 
 | ||||
|   parser.add_argument("route_name", type=(lambda x: x.replace("#", "|")), nargs="?", | ||||
|                       help="The route whose messages will be published.") | ||||
|   parser.add_argument("data_dir", nargs='?', default=os.getenv('UNLOGGER_DATA_DIR'), | ||||
|           help="Path to directory in which log and camera files are located.") | ||||
| 
 | ||||
|   parser.add_argument("--no-loop", action="store_true", help="Stop at the end of the replay.") | ||||
| 
 | ||||
|   def key_value_pair(x): | ||||
|     return x.split("=") | ||||
| 
 | ||||
|   parser.add_argument("address_mapping", nargs="*", type=key_value_pair, | ||||
|       help="Pairs <service>=<zmq_addr> to publish <service> on <zmq_addr>.") | ||||
| 
 | ||||
|   def comma_list(x): | ||||
|     return x.split(",") | ||||
| 
 | ||||
|   to_mock_group = parser.add_mutually_exclusive_group() | ||||
|   to_mock_group.add_argument("--min", action="store_true", default=os.getenv("MIN")) | ||||
|   to_mock_group.add_argument("--enabled", default=os.getenv("ENABLED"), type=comma_list) | ||||
| 
 | ||||
|   parser.add_argument("--disabled", type=comma_list, default=os.getenv("DISABLED") or ()) | ||||
| 
 | ||||
|   parser.add_argument( | ||||
|     "--tl", dest="publish_time_length", type=float, default=None, | ||||
|     help="Length of interval in event time for which messages should be published.") | ||||
| 
 | ||||
|   parser.add_argument( | ||||
|     "--no-realtime", dest="realtime", action="store_false", default=True, | ||||
|     help="Publish messages as quickly as possible instead of realtime.") | ||||
| 
 | ||||
|   parser.add_argument( | ||||
|     "--no-interactive", dest="interactive", action="store_false", default=True, | ||||
|     help="Disable interactivity.") | ||||
| 
 | ||||
|   parser.add_argument( | ||||
|     "--bind-early", action="store_true", default=False, | ||||
|     help="Bind early to avoid dropping messages.") | ||||
| 
 | ||||
|   parser.add_argument( | ||||
|     "--no-visionipc", action="store_true", default=False, | ||||
|     help="Do not output video over visionipc") | ||||
| 
 | ||||
|   parser.add_argument( | ||||
|     "--start-time", type=float, default=0., | ||||
|     help="Seek to this absolute time (in seconds) upon starting playback.") | ||||
| 
 | ||||
|   return parser | ||||
| 
 | ||||
| def main(argv): | ||||
|   args = get_arg_parser().parse_args(sys.argv[1:]) | ||||
| 
 | ||||
|   command_address = "ipc:///tmp/{}".format(uuid4()) | ||||
|   forward_commands_address = "ipc:///tmp/{}".format(uuid4()) | ||||
|   data_address = "ipc:///tmp/{}".format(uuid4()) | ||||
| 
 | ||||
|   address_mapping = _get_address_mapping(args) | ||||
| 
 | ||||
|   command_sock = zmq.Context.instance().socket(zmq.PUSH) | ||||
|   command_sock.connect(command_address) | ||||
| 
 | ||||
|   if args.route_name is not None: | ||||
|     route_name_split = args.route_name.split("|") | ||||
|     if len(route_name_split) > 1: | ||||
|       route_start_time = timestamp_to_s(route_name_split[1]) | ||||
|     else: | ||||
|       route_start_time = 0 | ||||
|     command_sock.send_pyobj( | ||||
|       SetRoute(args.route_name, args.start_time, args.data_dir)) | ||||
|   else: | ||||
|     print("waiting for external command...") | ||||
|     route_start_time = 0 | ||||
| 
 | ||||
|   subprocesses = {} | ||||
|   try: | ||||
|     subprocesses["data"] = multiprocessing.Process( | ||||
|       target=UnloggerWorker().run, | ||||
|       args=(forward_commands_address, data_address, address_mapping.copy())) | ||||
| 
 | ||||
|     subprocesses["control"] = multiprocessing.Process( | ||||
|       target=unlogger_thread, | ||||
|       args=(command_address, forward_commands_address, data_address, args.realtime, | ||||
|             _get_address_mapping(args), args.publish_time_length, args.bind_early, args.no_loop, args.no_visionipc)) | ||||
| 
 | ||||
|     subprocesses["data"].start() | ||||
|     subprocesses["control"].start() | ||||
| 
 | ||||
|     # Exit if any of the children die. | ||||
|     def exit_if_children_dead(*_): | ||||
|       for _, p in subprocesses.items(): | ||||
|         if not p.is_alive(): | ||||
|           [p.terminate() for p in subprocesses.values()] | ||||
|           exit() | ||||
|       signal.signal(signal.SIGCHLD, signal.SIG_IGN) | ||||
|     signal.signal(signal.SIGCHLD, exit_if_children_dead) | ||||
| 
 | ||||
|     if args.interactive: | ||||
|       keyboard_controller_thread(command_sock, route_start_time) | ||||
|     else: | ||||
|       # Wait forever for children. | ||||
|       while True: | ||||
|         time.sleep(10000.) | ||||
|   finally: | ||||
|     for p in subprocesses.values(): | ||||
|       if p.is_alive(): | ||||
|         try: | ||||
|           p.join(3.) | ||||
|         except multiprocessing.TimeoutError: | ||||
|           p.terminate() | ||||
|           continue | ||||
|   return 0 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|   sys.exit(main(sys.argv[1:])) | ||||
					Loading…
					
					
				
		Reference in new issue