Skip to content

Commit

Permalink
plotjuggler: add mac support (#23344)
Browse files Browse the repository at this point in the history
* plotjuggler: add mac support

* fix test?

* update readme

* oops

* fix

* cleanup

* typo

* works

* little more

* fix test

* little faster
  • Loading branch information
adeebshihadeh authored Jan 1, 2022
1 parent 8fe9c0e commit c26f294
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 53 deletions.
8 changes: 4 additions & 4 deletions tools/plotjuggler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@

## Installation

**NOTE: this is Ubuntu only for now. Pull requests for macOS support are welcome.**

Once you've cloned and are in openpilot, this command will download PlotJuggler and install our plugins:

`cd tools/plotjuggler && ./install.sh`
`cd tools/plotjuggler && ./juggle.py --install`

## Usage

```
$ ./juggle.py -h
usage: juggle.py [-h] [--demo] [--qlog] [--can] [--stream] [--layout [LAYOUT]] [route_name] [segment_number] [segment_count]
usage: juggle.py [-h] [--demo] [--qlog] [--can] [--stream] [--layout [LAYOUT]] [--install]
[route_name] [segment_number] [segment_count]
A helper to run PlotJuggler on openpilot routes
Expand All @@ -30,6 +29,7 @@ optional arguments:
--can Parse CAN data (default: False)
--stream Start PlotJuggler in streaming mode (default: False)
--layout [LAYOUT] Run PlotJuggler with a pre-defined layout (default: None)
--install Install or update PlotJuggler + plugins (default: False)
```

Example:
Expand Down
10 changes: 0 additions & 10 deletions tools/plotjuggler/install.sh

This file was deleted.

77 changes: 53 additions & 24 deletions tools/plotjuggler/juggle.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
import os
import sys
import multiprocessing
import platform
import shutil
import subprocess
import tarfile
import tempfile
import requests
import argparse
from tempfile import NamedTemporaryFile

from common.basedir import BASEDIR
from selfdrive.test.process_replay.compare_logs import save_log
Expand All @@ -17,9 +21,32 @@
juggle_dir = os.path.dirname(os.path.realpath(__file__))

DEMO_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36"
RELEASES_URL="https://github.com/commaai/PlotJuggler/releases/download/latest"
INSTALL_DIR = os.path.join(juggle_dir, "bin")


def install():
m = f"{platform.system()}-{platform.machine()}"
supported = ("Linux-x86_64", "Darwin-arm64", "Darwin-x86_64")
if m not in supported:
raise Exception(f"Unsupported platform: '{m}'. Supported platforms: {supported}")

if os.path.exists(INSTALL_DIR):
shutil.rmtree(INSTALL_DIR)
os.mkdir(INSTALL_DIR)

url = os.path.join(RELEASES_URL, m + ".tar.gz")
with requests.get(url, stream=True) as r, tempfile.NamedTemporaryFile() as tmp:
r.raise_for_status()
with open(tmp.name, 'wb') as tmpf:
for chunk in r.iter_content(chunk_size=1024*1024):
tmpf.write(chunk)

with tarfile.open(tmp.name) as tar:
tar.extractall(path=INSTALL_DIR)


def load_segment(segment_name):
print(f"Loading {segment_name}")
if segment_name is None:
return []

Expand All @@ -29,25 +56,26 @@ def load_segment(segment_name):
print(f"Error parsing {segment_name}: {e}")
return []


def start_juggler(fn=None, dbc=None, layout=None):
env = os.environ.copy()
env["BASEDIR"] = BASEDIR
pj = os.getenv("PLOTJUGGLER_PATH", os.path.join(juggle_dir, "bin/plotjuggler"))

env["PATH"] = f"{INSTALL_DIR}:{os.getenv('PATH', '')}"
if dbc:
env["DBC_NAME"] = dbc

extra_args = []
extra_args = ""
if fn is not None:
extra_args.append(f'-d {fn}')

extra_args += f"-d {fn}"
if layout is not None:
extra_args.append(f'-l {layout}')
extra_args += f"-l {layout}"

subprocess.call(f'plotjuggler --plugin_folders {INSTALL_DIR} {extra_args}',
shell=True, env=env, cwd=juggle_dir)

extra_args = " ".join(extra_args)
subprocess.call(f'{pj} --plugin_folders {os.path.join(juggle_dir, "bin")} {extra_args}', shell=True, env=env, cwd=juggle_dir)

def juggle_route(route_name, segment_number, segment_count, qlog, can, layout):
# TODO: abstract out the cabana stuff
if 'cabana' in route_name:
query = parse_qs(urlparse(route_name).query)
api = CommaApi(get_token())
Expand All @@ -62,8 +90,8 @@ def juggle_route(route_name, segment_number, segment_count, qlog, can, layout):
logs = logs[segment_number:segment_number+segment_count]

if None in logs:
fallback_answer = input("At least one of the rlogs in this segment does not exist, would you like to use the qlogs? (y/n) : ")
if fallback_answer == 'y':
ans = input(f"{logs.count(None)}/{len(logs)} of the rlogs in this segment are missing, would you like to fall back to the qlogs? (y/n) ")
if ans == 'y':
logs = r.qlog_paths()
if segment_number is not None:
logs = logs[segment_number:segment_number+segment_count]
Expand All @@ -85,17 +113,17 @@ def juggle_route(route_name, segment_number, segment_count, qlog, can, layout):
try:
DBC = __import__(f"selfdrive.car.{cp.carParams.carName}.values", fromlist=['DBC']).DBC
dbc = DBC[cp.carParams.carFingerprint]['pt']
except (ImportError, KeyError, AttributeError):
except Exception:
pass
break

tempfile = NamedTemporaryFile(suffix='.rlog', dir=juggle_dir)
save_log(tempfile.name, all_data, compress=False)
del all_data
with tempfile.NamedTemporaryFile(suffix='.rlog', dir=juggle_dir) as tmp:
save_log(tmp.name, all_data, compress=False)
del all_data
start_juggler(tmp.name, dbc, layout)

start_juggler(tempfile.name, dbc, layout)

def get_arg_parser():
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="A helper to run PlotJuggler on openpilot routes",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)

Expand All @@ -104,17 +132,18 @@ def get_arg_parser():
parser.add_argument("--can", action="store_true", help="Parse CAN data")
parser.add_argument("--stream", action="store_true", help="Start PlotJuggler in streaming mode")
parser.add_argument("--layout", nargs='?', help="Run PlotJuggler with a pre-defined layout")
parser.add_argument("--install", action="store_true", help="Install or update PlotJuggler + plugins")
parser.add_argument("route_name", nargs='?', help="The route name to plot (cabana share URL accepted)")
parser.add_argument("segment_number", type=int, nargs='?', help="The index of the segment to plot")
parser.add_argument("segment_count", type=int, nargs='?', help="The number of segments to plot", default=1)
return parser

if __name__ == "__main__":
arg_parser = get_arg_parser()
if len(sys.argv) == 1:
arg_parser.print_help()
parser.print_help()
sys.exit()
args = parser.parse_args()

if args.install:
install()
sys.exit()
args = arg_parser.parse_args(sys.argv[1:])

if args.stream:
start_juggler(layout=args.layout)
Expand Down
23 changes: 8 additions & 15 deletions tools/plotjuggler/test_plotjuggler.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,20 @@

from common.basedir import BASEDIR
from common.timeout import Timeout
from selfdrive.test.openpilotci import get_url
from tools.plotjuggler.juggle import install

class TestPlotJuggler(unittest.TestCase):

def test_install(self):
exit_code = os.system(os.path.join(BASEDIR, "tools/plotjuggler/install.sh"))
self.assertEqual(exit_code, 0)
def test_demo(self):
install()

def test_run(self):
pj = os.path.join(BASEDIR, "tools/plotjuggler/juggle.py")
p = subprocess.Popen(f'QT_QPA_PLATFORM=offscreen {pj} --demo None 1 --qlog',
stderr=subprocess.PIPE, shell=True, start_new_session=True)

test_url = get_url("ffccc77938ddbc44|2021-01-04--16-55-41", 0)

# Launch PlotJuggler with the executable in the bin directory
os.environ["PLOTJUGGLER_PATH"] = f'{os.path.join(BASEDIR, "tools/plotjuggler/bin/plotjuggler")}'
p = subprocess.Popen(f'QT_QPA_PLATFORM=offscreen {os.path.join(BASEDIR, "tools/plotjuggler/juggle.py")} \
"{test_url}"', stderr=subprocess.PIPE, shell=True,
start_new_session=True)

# Wait max 60 seconds for the "Done reading Rlog data" signal from the plugin
# Wait for "Done reading Rlog data" signal from the plugin
output = "\n"
with Timeout(120, error_msg=output):
with Timeout(180, error_msg=output):
while output.splitlines()[-1] != "Done reading Rlog data":
output += p.stderr.readline().decode("utf-8")

Expand Down

0 comments on commit c26f294

Please sign in to comment.