Merge remote-tracking branch 'upstream/master' into toyota-steer-faults

pull/24067/head
Shane Smiskol 3 years ago
commit d17c13087b
  1. 2
      .github/workflows/prebuilt.yaml
  2. 1
      Pipfile
  3. 24
      Pipfile.lock
  4. 1
      selfdrive/common/params.cc
  5. 2
      selfdrive/controls/controlsd.py
  6. 7
      selfdrive/manager/manager.py
  7. 22
      selfdrive/manager/process.py
  8. 24
      selfdrive/manager/process_config.py
  9. 147
      selfdrive/ui/qt/body.cc
  10. 21
      selfdrive/ui/qt/body.h
  11. 2
      selfdrive/ui/qt/home.cc
  12. 14
      selfdrive/ui/qt/maps/map.cc
  13. 12
      selfdrive/ui/qt/onroad.cc
  14. 2
      selfdrive/ui/qt/widgets/cameraview.cc
  15. 22
      selfdrive/ui/tests/body.py
  16. 2
      selfdrive/ui/ui.cc
  17. 11
      tools/joystick/README.md
  18. 54
      tools/latencylogger/latency_logger.py

@ -3,6 +3,8 @@ on:
schedule:
- cron: '0 * * * *'
workflow_dispatch:
env:
BASE_IMAGE: openpilot-base
DOCKER_REGISTRY: ghcr.io/commaai

@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
azure-storage-blob = "~=2.1"
control = "*"
coverage = "*"
dictdiffer = "*"

24
Pipfile.lock generated

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "41e285b10b9b5a353b3eb9c202886e52d22403c369784877aaa35e099aa203a8"
"sha256": "918c8cba5c6a0242dc0f6ea74246176a1c54f0a9395feddf35af2189cc813378"
},
"pipfile-spec": 6,
"requires": {
@ -1111,6 +1111,28 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.4.0"
},
"azure-common": {
"hashes": [
"sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3",
"sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad"
],
"version": "==1.1.28"
},
"azure-storage-blob": {
"hashes": [
"sha256:a8e91a51d4f62d11127c7fd8ba0077385c5b11022f0269f8a2a71b9fc36bef31",
"sha256:b90323aad60f207f9f90a0c4cf94c10acc313c20b39403398dfba51f25f7b454"
],
"index": "pypi",
"version": "==2.1.0"
},
"azure-storage-common": {
"hashes": [
"sha256:b01a491a18839b9d05a4fe3421458a0ddb5ab9443c14e487f40d16f9a1dc2fbe",
"sha256:ccedef5c67227bc4d6670ffd37cec18fb529a1b7c3a5e53e4096eb0cf23dc73f"
],
"version": "==2.1.0"
},
"babel": {
"hashes": [
"sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9",

@ -94,6 +94,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CompletedTrainingVersion", PERSISTENT},
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisablePowerDown", PERSISTENT},
{"DisableRadar_Allow", PERSISTENT},
{"DisableRadar", PERSISTENT}, // WARNING: THIS DISABLES AEB

@ -343,7 +343,7 @@ class Controls:
# Check for mismatch between openpilot and car's PCM
cruise_mismatch = CS.cruiseState.enabled and (not self.enabled or not self.CP.pcmCruise)
self.cruise_mismatch_counter = self.cruise_mismatch_counter + 1 if cruise_mismatch else 0
if self.cruise_mismatch_counter > int(3. / DT_CTRL):
if self.cruise_mismatch_counter > int(6. / DT_CTRL):
self.events.add(EventName.cruiseMismatch)
# Check for FCW

@ -128,17 +128,16 @@ def manager_thread() -> None:
ignore.append("pandad")
ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0]
ensure_running(managed_processes.values(), started=False, not_run=ignore)
sm = messaging.SubMaster(['deviceState', 'carParams'], poll=['deviceState'])
pm = messaging.PubMaster(['managerState'])
ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore)
while True:
sm.update()
started = sm['deviceState'].started
driverview = params.get_bool("IsDriverViewEnabled")
ensure_running(managed_processes.values(), started=started, driverview=driverview, notcar=sm['carParams'].notCar, not_run=ignore)
ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore)
running = ' '.join("%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
for p in managed_processes.values() if p.proc)

@ -4,7 +4,7 @@ import signal
import struct
import time
import subprocess
from typing import Optional, List, ValuesView
from typing import Optional, Callable, List, ValuesView
from abc import ABC, abstractmethod
from multiprocessing import Process
@ -12,6 +12,7 @@ from setproctitle import setproctitle # pylint: disable=no-name-in-module
import cereal.messaging as messaging
import selfdrive.sentry as sentry
from cereal import car
from common.basedir import BASEDIR
from common.params import Params
from common.realtime import sec_since_boot
@ -71,8 +72,7 @@ class ManagerProcess(ABC):
sigkill = False
onroad = True
offroad = False
driverview = False
notcar = False
callback: Optional[Callable[[bool, Params, car.CarParams], bool]] = None
proc: Optional[Process] = None
enabled = True
name = ""
@ -184,15 +184,14 @@ class ManagerProcess(ABC):
class NativeProcess(ManagerProcess):
def __init__(self, name, cwd, cmdline, enabled=True, onroad=True, offroad=False, driverview=False, notcar=False, unkillable=False, sigkill=False, watchdog_max_dt=None):
def __init__(self, name, cwd, cmdline, enabled=True, onroad=True, offroad=False, callback=None, unkillable=False, sigkill=False, watchdog_max_dt=None):
self.name = name
self.cwd = cwd
self.cmdline = cmdline
self.enabled = enabled
self.onroad = onroad
self.offroad = offroad
self.driverview = driverview
self.notcar = notcar
self.callback = callback
self.unkillable = unkillable
self.sigkill = sigkill
self.watchdog_max_dt = watchdog_max_dt
@ -217,14 +216,13 @@ class NativeProcess(ManagerProcess):
class PythonProcess(ManagerProcess):
def __init__(self, name, module, enabled=True, onroad=True, offroad=False, driverview=False, notcar=False, unkillable=False, sigkill=False, watchdog_max_dt=None):
def __init__(self, name, module, enabled=True, onroad=True, offroad=False, callback=None, unkillable=False, sigkill=False, watchdog_max_dt=None):
self.name = name
self.module = module
self.enabled = enabled
self.onroad = onroad
self.offroad = offroad
self.driverview = driverview
self.notcar = notcar
self.callback = callback
self.unkillable = unkillable
self.sigkill = sigkill
self.watchdog_max_dt = watchdog_max_dt
@ -291,7 +289,7 @@ class DaemonProcess(ManagerProcess):
pass
def ensure_running(procs: ValuesView[ManagerProcess], started: bool, driverview: bool=False, notcar: bool=False,
def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None, CP: car.CarParams=None,
not_run: Optional[List[str]]=None) -> None:
if not_run is None:
not_run = []
@ -301,9 +299,9 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, driverview:
run = any((
p.offroad and not started,
p.onroad and started,
p.driverview and driverview,
p.notcar and notcar,
))
if p.callback is not None and None not in (params, CP):
run = run or p.callback(started, params, CP)
# Conditions that block a process from starting
run = run and not any((

@ -1,18 +1,30 @@
import os
from cereal import car
from common.params import Params
from selfdrive.hardware import TICI, PC
from selfdrive.manager.process import PythonProcess, NativeProcess, DaemonProcess
WEBCAM = os.getenv("USE_WEBCAM") is not None
def driverview(started: bool, params: Params, CP: car.CarParams) -> bool:
return params.get_bool("IsDriverViewEnabled")
def notcar(started: bool, params: Params, CP: car.CarParams) -> bool:
return CP.notCar
def logging(started, params, CP: car.CarParams) -> bool:
run = (not CP.notCar) or not params.get_bool("DisableLogging")
return started and run
procs = [
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
# due to qualcomm kernel bugs SIGKILLing camerad sometimes causes page table corruption
NativeProcess("camerad", "selfdrive/camerad", ["./camerad"], unkillable=True, driverview=True),
NativeProcess("camerad", "selfdrive/camerad", ["./camerad"], unkillable=True, callback=driverview),
NativeProcess("clocksd", "selfdrive/clocksd", ["./clocksd"]),
NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld"], enabled=(not PC or WEBCAM), driverview=True),
NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld"], enabled=(not PC or WEBCAM), callback=driverview),
NativeProcess("logcatd", "selfdrive/logcatd", ["./logcatd"]),
NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"]),
NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"], onroad=False, callback=logging),
NativeProcess("modeld", "selfdrive/modeld", ["./modeld"]),
NativeProcess("navd", "selfdrive/ui/navd", ["./navd"], offroad=True),
NativeProcess("proclogd", "selfdrive/proclogd", ["./proclogd"]),
@ -25,7 +37,7 @@ procs = [
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd"),
PythonProcess("controlsd", "selfdrive.controls.controlsd"),
PythonProcess("deleter", "selfdrive.loggerd.deleter", offroad=True),
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", enabled=(not PC or WEBCAM), driverview=True),
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", enabled=(not PC or WEBCAM), callback=driverview),
PythonProcess("logmessaged", "selfdrive.logmessaged", offroad=True),
PythonProcess("pandad", "selfdrive.boardd.pandad", offroad=True),
PythonProcess("paramsd", "selfdrive.locationd.paramsd"),
@ -38,8 +50,8 @@ procs = [
PythonProcess("uploader", "selfdrive.loggerd.uploader", offroad=True),
PythonProcess("statsd", "selfdrive.statsd", offroad=True),
NativeProcess("bridge", "cereal/messaging", ["./bridge"], onroad=False, notcar=True),
PythonProcess("webjoystick", "tools.joystick.web", onroad=False, notcar=True),
NativeProcess("bridge", "cereal/messaging", ["./bridge"], onroad=False, callback=notcar),
PythonProcess("webjoystick", "tools.joystick.web", onroad=False, callback=notcar),
# Experimental
PythonProcess("rawgpsd", "selfdrive.sensord.rawgps.rawgpsd", enabled=os.path.isfile("/persist/comma/use-quectel-rawgps")),

@ -1,48 +1,132 @@
#include "selfdrive/ui/qt/body.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QStackedLayout>
BodyWindow::BodyWindow(QWidget *parent) : QLabel(parent) {
#include "selfdrive/common/params.h"
#include "selfdrive/common/timing.h"
RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) {
setCheckable(true);
setChecked(false);
setFixedSize(148, 148);
QObject::connect(this, &QPushButton::toggled, [=]() {
setEnabled(false);
});
}
void RecordButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPoint center(width() / 2, height() / 2);
QColor bg(isChecked() ? "#FFFFFF" : "#737373");
QColor accent(isChecked() ? "#FF0000" : "#FFFFFF");
if (!isEnabled()) {
bg = QColor("#404040");
accent = QColor("#FFFFFF");
}
if (isDown()) {
accent.setAlphaF(0.7);
}
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(center, 74, 74);
p.setPen(QPen(accent, 6));
p.setBrush(Qt::NoBrush);
p.drawEllipse(center, 42, 42);
p.setPen(Qt::NoPen);
p.setBrush(accent);
p.drawEllipse(center, 22, 22);
}
BodyWindow::BodyWindow(QWidget *parent) : fuel_filter(1.0, 5., 1. / UI_FREQ), QWidget(parent) {
QStackedLayout *layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
QWidget *w = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout(w);
vlayout->setMargin(45);
layout->addWidget(w);
// face
face = new QLabel();
face->setAlignment(Qt::AlignCenter);
layout->addWidget(face);
awake = new QMovie("../assets/body/awake.gif");
awake->setCacheMode(QMovie::CacheAll);
sleep = new QMovie("../assets/body/sleep.gif");
sleep->setCacheMode(QMovie::CacheAll);
QPalette p(Qt::black);
setPalette(p);
setAutoFillBackground(true);
setAlignment(Qt::AlignCenter);
setAttribute(Qt::WA_TransparentForMouseEvents, true);
// record button
btn = new RecordButton(this);
vlayout->addWidget(btn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(btn, &QPushButton::clicked, [=](bool checked) {
btn->setEnabled(false);
Params().putBool("DisableLogging", !checked);
last_button = nanos_since_boot();
});
w->raise();
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
}
void BodyWindow::paintEvent(QPaintEvent *event) {
QLabel::paintEvent(event);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.fillRect(rect(), QColor(0, 0, 0));
// battery outline + detail
p.translate(width() - 136, 16);
const QColor gray = QColor("#737373");
p.setBrush(Qt::NoBrush);
p.setPen(QPen(gray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
p.drawRoundedRect(2, 2, 78, 36, 8, 8);
p.setPen(Qt::NoPen);
p.setBrush(gray);
p.drawRoundedRect(84, 12, 6, 16, 4, 4);
p.drawRect(84, 12, 3, 16);
// draw battery level
const int offset = 90;
const int radius = 60 / 2;
const int levels = 5;
const float interval = 1. / levels;
for (int i = 0; i < levels; i++) {
float level = 1.0 - (i+1)*interval;
float perc = (fuel >= level) ? 1.0 : 0.35;
p.setBrush(QColor(255, 255, 255, 255 * perc));
QPoint pt(width() - (i*offset + offset / 2), offset / 2);
p.drawEllipse(pt, radius, radius);
// battery level
double fuel = std::clamp(fuel_filter.x(), 0.2f, 1.0f);
const int m = 5; // manual margin since we can't do an inner border
p.setPen(Qt::NoPen);
p.setBrush(fuel > 0.25 ? QColor("#32D74B") : QColor("#FF453A"));
p.drawRoundedRect(2 + m, 2 + m, (78 - 2*m)*fuel, 36 - 2*m, 4, 4);
// charging status
if (charging) {
p.setPen(Qt::NoPen);
p.setBrush(Qt::white);
const QPolygonF charger({
QPointF(12.31, 0),
QPointF(12.31, 16.92),
QPointF(18.46, 16.92),
QPointF(6.15, 40),
QPointF(6.15, 23.08),
QPointF(0, 23.08),
});
p.drawPolygon(charger.translated(98, 0));
}
}
void BodyWindow::offroadTransition(bool offroad) {
btn->setChecked(true);
btn->setEnabled(true);
fuel_filter.reset(1.0);
}
void BodyWindow::updateState(const UIState &s) {
if (!isVisible()) {
@ -52,14 +136,25 @@ void BodyWindow::updateState(const UIState &s) {
const SubMaster &sm = *(s.sm);
auto cs = sm["carState"].getCarState();
fuel = cs.getFuelGauge();
charging = cs.getCharging();
fuel_filter.update(cs.getFuelGauge());
// TODO: use carState.standstill when that's fixed
const bool standstill = std::abs(cs.getVEgo()) < 0.01;
QMovie *m = standstill ? sleep : awake;
if (m != movie()) {
setMovie(m);
movie()->start();
if (m != face->movie()) {
face->setMovie(m);
face->movie()->start();
}
// update record button state
if (sm.updated("managerState") && (sm.rcv_time("managerState") - last_button)*1e-9 > 0.5) {
for (auto proc : sm["managerState"].getManagerState().getProcesses()) {
if (proc.getName() == "loggerd") {
btn->setEnabled(true);
btn->setChecked(proc.getRunning());
}
}
}
update();

@ -2,20 +2,37 @@
#include <QMovie>
#include <QLabel>
#include <QPushButton>
#include "selfdrive/common/util.h"
#include "selfdrive/ui/ui.h"
class BodyWindow : public QLabel {
class RecordButton : public QPushButton {
Q_OBJECT
public:
RecordButton(QWidget* parent = 0);
private:
void paintEvent(QPaintEvent*) override;
};
class BodyWindow : public QWidget {
Q_OBJECT
public:
BodyWindow(QWidget* parent = 0);
private:
float fuel = 1.0;
bool charging = false;
uint64_t last_button = 0;
FirstOrderFilter fuel_filter;
QLabel *face;
QMovie *awake, *sleep;
RecordButton *btn;
void paintEvent(QPaintEvent*) override;
private slots:
void updateState(const UIState &s);
void offroadTransition(bool onroad);
};

@ -32,7 +32,6 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
body = new BodyWindow(this);
slayout->addWidget(body);
body->setEnabled(false);
driver_view = new DriverViewWindow(this);
connect(driver_view, &DriverViewWindow::done, [=] {
@ -59,6 +58,7 @@ void HomeWindow::updateState(const UIState &s) {
}
void HomeWindow::offroadTransition(bool offroad) {
body->setEnabled(false);
sidebar->setVisible(offroad);
if (offroad) {
slayout->setCurrentWidget(home);

@ -117,17 +117,15 @@ void MapWindow::timerUpdate() {
auto location = (*sm)["liveLocationKalman"].getLiveLocationKalman();
auto pos = location.getPositionGeodetic();
auto orientation = location.getCalibratedOrientationNED();
auto velocity = location.getVelocityCalibrated();
localizer_valid = (location.getStatus() == cereal::LiveLocationKalman::Status::VALID) && pos.getValid();
localizer_valid = (location.getStatus() == cereal::LiveLocationKalman::Status::VALID) &&
pos.getValid() && orientation.getValid() && velocity.getValid();
if (localizer_valid) {
float velocity = location.getVelocityCalibrated().getValue()[0];
float bearing = RAD2DEG(orientation.getValue()[2]);
auto coordinate = QMapbox::Coordinate(pos.getValue()[0], pos.getValue()[1]);
last_position = coordinate;
last_bearing = bearing;
velocity_filter.update(velocity);
last_position = QMapbox::Coordinate(pos.getValue()[0], pos.getValue()[1]);
last_bearing = RAD2DEG(orientation.getValue()[2]);
velocity_filter.update(velocity.getValue()[0]);
}
}

@ -294,6 +294,8 @@ void NvgWindow::updateFrameMat(int w, int h) {
}
void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) {
painter.save();
const UIScene &scene = s->scene;
// lanelines
for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) {
@ -329,9 +331,13 @@ void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) {
}
painter.setBrush(bg);
painter.drawPolygon(scene.track_vertices.v, scene.track_vertices.cnt);
painter.restore();
}
void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd) {
painter.save();
const float speedBuff = 10.;
const float leadBuff = 40.;
const float d_rel = lead_data.getX()[0];
@ -361,6 +367,8 @@ void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV
QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}};
painter.setBrush(redColor(fillAlpha));
painter.drawPolygon(chevron, std::size(chevron));
painter.restore();
}
void NvgWindow::paintGL() {
@ -370,8 +378,6 @@ void NvgWindow::paintGL() {
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
drawHud(painter);
UIState *s = uiState();
if (s->worldObjectsVisible()) {
@ -388,6 +394,8 @@ void NvgWindow::paintGL() {
}
}
drawHud(painter);
double cur_draw_t = millis_since_boot();
double dt = cur_draw_t - prev_draw_t;
double fps = fps_filter.update(1. / dt * 1000);

@ -216,6 +216,7 @@ void CameraViewWidget::paintGL() {
if (latest_frame == nullptr) return;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glViewport(0, 0, width(), height());
glBindVertexArray(frame_vao);
@ -238,6 +239,7 @@ void CameraViewWidget::paintGL() {
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
void CameraViewWidget::vipcConnected(VisionIpcClient *vipc_client) {

@ -0,0 +1,22 @@
#!/usr/bin/env python3
import time
import cereal.messaging as messaging
if __name__ == "__main__":
while True:
pm = messaging.PubMaster(['carParams', 'carState'])
batt = 1.
while True:
msg = messaging.new_message('carParams')
msg.carParams.carName = "COMMA BODY"
msg.carParams.notCar = True
pm.send('carParams', msg)
for b in range(100, 0, -1):
msg = messaging.new_message('carState')
msg.carState.charging = True
msg.carState.fuelGauge = b / 100.
pm.send('carState', msg)
time.sleep(0.1)
time.sleep(1)

@ -233,7 +233,7 @@ UIState::UIState(QObject *parent) : QObject(parent) {
sm = std::make_unique<SubMaster, const std::initializer_list<const char *>>({
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "roadCameraState",
"pandaStates", "carParams", "driverMonitoringState", "sensorEvents", "carState", "liveLocationKalman",
"wideRoadCameraState",
"wideRoadCameraState", "managerState",
});
Params params;

@ -45,6 +45,17 @@ In order to use a joystick over the network, we need to run joystickd locally fr
tools/joystick/joystickd.py
```
### Web joystick on your mobile device
A browser-based virtual joystick designed for touch screens. Starts automatically when installed on comma body (non-car robotics platform).
For cars, start the web joystick service manually via SSH before starting the car.
```shell
tools/joystick/web.py
```
After starting the car/body, open the web joystick app at this URL: `http://[comma three IP address]:5000`
---
Now start your car and openpilot should go into joystick mode with an alert on startup! The status of the axes will display on the alert, while button statuses print in the shell.

@ -7,7 +7,7 @@ import sys
from collections import defaultdict
from tools.lib.logreader import logreader_from_route_or_segment
DEMO_ROUTE = "9f583b1d93915c31|2022-04-26--18-49-49"
SERVICES = ['camerad', 'modeld', 'plannerd', 'controlsd', 'boardd']
@ -25,7 +25,7 @@ MSGQ_TO_SERVICE = {
SERVICE_TO_DURATIONS = {
'camerad': ['processingTime'],
'modeld': ['modelExecutionTime', 'gpuExecutionTime'],
'plannerd': ["solverExecutionTime"],
'plannerd': ['solverExecutionTime'],
}
def read_logs(lr):
@ -38,7 +38,7 @@ def read_logs(lr):
if msg.which() == 'sendcan':
latest_sendcan_monotime = msg.logMonoTime
continue
if msg.which() in MSGQ_TO_SERVICE:
service = MSGQ_TO_SERVICE[msg.which()]
msg_obj = getattr(msg, msg.which())
@ -83,8 +83,10 @@ def read_logs(lr):
if msg_obj.frameIdExtra != frame_id:
frame_mismatches.append(frame_id)
assert frame_id_fails < 20, "Too many frameId fetch fails"
assert len(frame_mismatches) < 20, "Too many frame mismatches"
if frame_id_fails > 20:
print("Warning, many frameId fetch fails", frame_id_fails)
if len(frame_mismatches) > 20:
print("Warning, many frame mismatches", len(frame_mismatches))
return (data, frame_mismatches)
def find_frame_id(time, service, start_times, end_times):
@ -98,15 +100,15 @@ def find_t0(start_times, frame_id=-1):
m = max(start_times.keys())
while frame_id <= m:
for service in SERVICES:
if service in start_times[frame_id]:
if start_times[frame_id][service]:
return start_times[frame_id][service]
frame_id += 1
raise Exception('No start time has been set')
## ASSUMES THAT AT LEAST ONE CLOUDLOG IS MADE IN CONTROLSD
def insert_cloudlogs(lr, timestamps, start_times, end_times):
t0 = find_t0(start_times)
# at least one cloudlog must be made in controlsd
t0 = find_t0(start_times)
failed_inserts = 0
latest_controls_frameid = 0
for msg in lr:
@ -117,7 +119,7 @@ def insert_cloudlogs(lr, timestamps, start_times, end_times):
service = jmsg['ctx']['daemon']
event = jmsg['msg']['timestamp']['event']
if time < t0:
# Filter out controlsd messages which arrive before the camera loop
# Filter out controlsd messages which arrive before the camera loop
continue
if "frame_id" in jmsg['msg']['timestamp']:
@ -138,40 +140,41 @@ def insert_cloudlogs(lr, timestamps, start_times, end_times):
timestamps[frame_id][service].append((event, time))
else:
failed_inserts += 1
assert latest_controls_frameid > 0, "No timestamp in controlsd"
assert failed_inserts < len(timestamps), "Too many failed cloudlog inserts"
if latest_controls_frameid == 0:
print("Warning: failed to bind boardd logs to a frame ID. Add a timestamp cloudlog in controlsd.")
elif failed_inserts > len(timestamps):
print(f"Warning: failed to bind {failed_inserts} cloudlog timestamps to a frame ID")
def print_timestamps(timestamps, durations, start_times, relative):
t0 = find_t0(start_times)
t0 = find_t0(start_times)
for frame_id in timestamps.keys():
print('='*80)
print("Frame ID:", frame_id)
if relative:
t0 = find_t0(start_times, frame_id)
t0 = find_t0(start_times, frame_id)
for service in SERVICES:
print(" "+service)
print(" "+service)
events = timestamps[frame_id][service]
for event, time in sorted(events, key = lambda x: x[1]):
print(" "+'%-53s%-53s' %(event, str((time-t0)/1e6)))
print(" "+'%-53s%-53s' %(event, str((time-t0)/1e6)))
for event, time in durations[frame_id][service]:
print(" "+'%-53s%-53s' %(event, str(time*1000)))
print(" "+'%-53s%-53s' %(event, str(time*1000)))
def graph_timestamps(timestamps, start_times, end_times, relative):
t0 = find_t0(start_times)
y0 = min(start_times.keys())
t0 = find_t0(start_times)
fig, ax = plt.subplots()
ax.set_xlim(0, 150 if relative else 750)
ax.set_ylim(y0, y0+15)
ax.set_ylim(0, 15)
ax.set_xlabel('milliseconds')
ax.set_ylabel('Frame ID')
colors = ['blue', 'green', 'red', 'yellow', 'purple']
assert len(colors) == len(SERVICES), 'Each service needs a color'
points = {"x": [], "y": [], "labels": []}
for frame_id, services in timestamps.items():
for i, (frame_id, services) in enumerate(timestamps.items()):
if relative:
t0 = find_t0(start_times, frame_id)
t0 = find_t0(start_times, frame_id)
service_bars = []
for service, events in services.items():
if start_times[frame_id][service] and end_times[frame_id][service]:
@ -180,9 +183,9 @@ def graph_timestamps(timestamps, start_times, end_times, relative):
service_bars.append(((start-t0)/1e6,(end-start)/1e6))
for event in events:
points['x'].append((event[1]-t0)/1e6)
points['y'].append(frame_id)
points['y'].append(i)
points['labels'].append(event[0])
ax.broken_barh(service_bars, (frame_id-0.45, 0.9), facecolors=(colors), alpha=0.5)
ax.broken_barh(service_bars, (i-0.45, 0.9), facecolors=(colors), alpha=0.5)
scatter = ax.scatter(points['x'], points['y'], marker='d', edgecolor='black')
tooltip = mpld3.plugins.PointLabelTooltip(scatter, labels=points['labels'])
@ -212,6 +215,7 @@ if __name__ == "__main__":
r = DEMO_ROUTE if args.demo else args.route_or_segment_name.strip()
lr = logreader_from_route_or_segment(r, sort_by_time=True)
data, _ = get_timestamps(lr)
print_timestamps(data['timestamp'], data['duration'], data['start'], args.relative)
if args.plot:

Loading…
Cancel
Save