Merge branch 'upstream/master' into sync-20260517-new-new

This commit is contained in:
Jason Wen
2026-06-02 22:59:13 -04:00
648 changed files with 5113 additions and 90864 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ source .venv/bin/activate
**4. Build openpilot**
``` bash
scons -u -j$(nproc)
scons -u
```
## WSL on Windows
+2 -2
View File
@@ -56,7 +56,7 @@ async def ping(request: 'web.Request'):
async def offer(request: 'web.Request'):
params = await request.json()
body = StreamRequestBody(params["sdp"], ["driver"], ["testJoystick"], ["carState"])
body = StreamRequestBody(params["sdp"], "driver", ["testJoystick"], ["carState"])
body_json = json.dumps(dataclasses.asdict(body))
logger.info("Sending offer to webrtcd...")
@@ -69,7 +69,7 @@ async def offer(request: 'web.Request'):
def main():
# Enable joystick debug mode
Params().put_bool("JoystickDebugMode", True)
Params().put_bool("JoystickDebugMode", True, block=True)
# App needs to be HTTPS for WebRTC to work on the browser
ssl_context = create_ssl_context()
+1
View File
@@ -1,5 +1,6 @@
moc_*
*.moc
*.generated.qrc
assets.cc
+13 -1
View File
@@ -2,6 +2,7 @@ import subprocess
import os
import shutil
import bootstrap_icons
import libusb
Import('env', 'arch', 'common', 'messaging', 'visionipc', 'cereal', 'replay_lib')
@@ -80,10 +81,21 @@ if arch != "Darwin":
opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../opendbc/dbc").abspath)
cabana_env['CXXFLAGS'] += [opendbc_path]
def write_assets_qrc(target, source, env):
with open(str(source[0])) as f:
qrc = f.read()
with open(str(target[0]), "w") as f:
f.write(qrc.replace("@BOOTSTRAP_ICONS_SVG@", str(bootstrap_icons.SVG_PATH)))
# build assets
assets = "assets/assets.cc"
assets_src = "assets/assets.qrc"
assets_src = cabana_env.Command(
"assets/assets.generated.qrc",
"assets/assets.qrc",
write_assets_qrc,
)
cabana_env.Command(assets, assets_src, f"rcc $SOURCES -o $TARGET")
cabana_env.Depends(assets_src, str(bootstrap_icons.SVG_PATH))
cabana_env.Depends(assets, Glob('/assets/*', exclude=[assets, assets_src, "assets/assets.o"]))
cabana_srcs = ['mainwin.cc', 'streams/pandastream.cc', 'streams/devicestream.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'historylog.cc', 'videowidget.cc', 'signalview.cc',
+1 -1
View File
@@ -1,6 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="bootstrap-icons.svg">../../../third_party/bootstrap/bootstrap-icons.svg</file>
<file alias="bootstrap-icons.svg">@BOOTSTRAP_ICONS_SVG@</file>
<file>cabana-icon.png</file>
</qresource>
</RCC>
+2 -2
View File
@@ -40,14 +40,14 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) {
addShortcuts();
setWhatsThis(R"(
<b>Binary View</b><br/>
<!-- TODO: add descprition here -->
<!-- TODO: add description here -->
<span style="color:gray">Shortcuts</span><br />
Delete Signal:
<span style="background-color:lightGray;color:gray">&nbsp;x&nbsp;</span>,
<span style="background-color:lightGray;color:gray">&nbsp;Backspace&nbsp;</span>,
<span style="background-color:lightGray;color:gray">&nbsp;Delete&nbsp;</span><br />
Change endianness: <span style="background-color:lightGray;color:gray">&nbsp;e&nbsp; </span><br />
Change singedness: <span style="background-color:lightGray;color:gray">&nbsp;s&nbsp;</span><br />
Change signedness: <span style="background-color:lightGray;color:gray">&nbsp;s&nbsp;</span><br />
Open chart:
<span style="background-color:lightGray;color:gray">&nbsp;c&nbsp;</span>,
<span style="background-color:lightGray;color:gray">&nbsp;p&nbsp;</span>,
+1 -1
View File
@@ -33,6 +33,6 @@ fi
# Build _cabana
cd "$ROOT"
scons -j4 tools/cabana/_cabana cereal/messaging/bridge
scons tools/cabana/_cabana cereal/messaging/bridge
exec "$DIR/_cabana" "$@"
+3 -3
View File
@@ -64,7 +64,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget
setWhatsThis(tr(R"(
<b>Message View</b><br/>
<!-- TODO: add descprition here -->
<!-- TODO: add description here -->
<span style="color:gray">Byte color</span><br />
<span style="color:gray;"> </span> constant changing<br />
<span style="color:blue;"> </span> increasing<br />
@@ -146,7 +146,7 @@ void MessagesWidget::menuAboutToShow() {
action->setCheckable(true);
action->setChecked(settings.multiple_lines_hex);
action = menu->addAction(tr("Show inactive Messages"), model, &MessageListModel::showInactivemessages);
action = menu->addAction(tr("Show inactive messages"), model, &MessageListModel::showInactiveMessages);
action->setCheckable(true);
action->setChecked(model->show_inactive_messages);
}
@@ -216,7 +216,7 @@ void MessageListModel::setFilterStrings(const QMap<int, QString> &filters) {
filterAndSort();
}
void MessageListModel::showInactivemessages(bool show) {
void MessageListModel::showInactiveMessages(bool show) {
show_inactive_messages = show;
filterAndSort();
}
+1 -1
View File
@@ -36,7 +36,7 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override { return items_.size(); }
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
void setFilterStrings(const QMap<int, QString> &filters);
void showInactivemessages(bool show);
void showInactiveMessages(bool show);
void msgsReceived(const std::set<MessageId> *new_msgs, bool has_new_ids);
bool filterAndSort();
void dbcModified();
+1 -1
View File
@@ -495,7 +495,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
setWhatsThis(tr(R"(
<b>Signal view</b><br />
<!-- TODO: add descprition here -->
<!-- TODO: add description here -->
)"));
}
+1 -1
View File
@@ -51,7 +51,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) {
updatePlayBtnState();
setWhatsThis(tr(R"(
<b>Video</b><br />
<!-- TODO: add descprition here -->
<!-- TODO: add description here -->
<span style="color:gray">Timeline color</span>
<table>
<tr><td><span style="color:%1;"> </span>Disengaged </td>
+2 -2
View File
@@ -219,7 +219,7 @@ def load_route_metadata(route):
params = Params()
for entry in init_data.params.entries:
try:
params.put(entry.key, params.cpp2python(entry.key, entry.value))
params.put(entry.key, params.cpp2python(entry.key, entry.value), block=True)
except UnknownKeyName:
pass
@@ -326,7 +326,7 @@ def clip(route: Route, output: str, start: int, end: int, headless: bool = True,
frame_idx = 0
with tqdm.tqdm(total=len(message_chunks), desc="Rendering", unit="frame") as pbar:
for should_render in gui_app.render():
for should_render, _, _ in gui_app.render():
if frame_idx >= len(message_chunks):
break
_, frame_bytes = frame_queue.get()
+1
View File
@@ -3,6 +3,7 @@ jot_*.o
*.o
jotpluggler
car_fingerprint_to_dbc.h
generated_event_extractors.h
generated_dbcs/.stamp
generated_dbcs/*.dbc
layouts/.jotpluggler_autosave/
+20
View File
@@ -1,4 +1,6 @@
import os
import subprocess
import bootstrap_icons
import imgui
import libusb
from opendbc import get_generated_dbcs
@@ -15,6 +17,7 @@ jot_env["CPPPATH"] += [imgui.INCLUDE_DIR, libusb.INCLUDE_DIR]
jot_env["CXXFLAGS"] += [
"-DGLFW_INCLUDE_NONE",
'-DJOTP_REPO_ROOT=\'"%s"\'' % os.path.realpath(BASEDIR),
'-DBOOTSTRAP_ICONS_TTF=\'"%s"\'' % bootstrap_icons.TTF_PATH,
]
def materialize_generated_dbcs(target, source, env):
@@ -77,8 +80,24 @@ def write_car_fingerprint_to_dbc_header(target, source, env):
return None
def generate_event_extractors(target, source, env):
subprocess.check_call([
"python3",
"tools/jotpluggler/generate_event_extractors.py",
os.path.realpath(BASEDIR),
str(target[0]),
])
return None
generated_dbc_stamp = jot_env.Command(f"generated_dbcs/.stamp", [], materialize_generated_dbcs)
car_fingerprint_to_dbc = jot_env.Command("car_fingerprint_to_dbc.h", [], write_car_fingerprint_to_dbc_header)
event_extractors = jot_env.Command("generated_event_extractors.h", [
"generate_event_extractors.py",
jot_env.Glob("#cereal/*.capnp"),
jot_env.Glob("#cereal/include/*.capnp"),
],
generate_event_extractors,
)
libs = [replay_lib, common, messaging, visionipc, cereal, File(f"{imgui.LIB_DIR}/libimgui.a"), File(f"{imgui.LIB_DIR}/libglfw3.a"),
"avformat", "avcodec", "avutil", "x264", "yuv", "z", "bz2", "zstd", "m", "pthread", "usb-1.0"]
@@ -90,3 +109,4 @@ else:
program = jot_env.Program("jotpluggler", jot_env.Glob("*.cc"), LIBS=libs)
jot_env.Depends(program, generated_dbc_stamp)
jot_env.Depends(program, car_fingerprint_to_dbc)
jot_env.Depends(program, event_extractors)
+2 -16
View File
@@ -27,7 +27,7 @@
#include <unordered_set>
#include <unistd.h>
#include "third_party/json11/json11.hpp"
#include "json11/json11.hpp"
namespace fs = std::filesystem;
@@ -324,7 +324,7 @@ void configure_style() {
plot_style.LegendInnerPadding = ImVec2(6.0f, 3.0f);
plot_style.LegendSpacing = ImVec2(7.0f, 2.0f);
plot_style.PlotPadding = ImVec2(4.0f, 8.0f);
plot_style.FitPadding = ImVec2(0.02f, 0.4f);
plot_style.FitPadding = ImVec2(0.02f, static_cast<float>(PLOT_Y_PADDING_FRACTION));
ImPlot::MapInputDefault();
ImPlotInputMap &input_map = ImPlot::GetInputMap();
@@ -1334,20 +1334,6 @@ bool apply_pane_menu_action(AppSession *session, UiState *state, int pane_index,
layout_changed = true;
success_status = "Plot view reset";
break;
case PaneMenuActionKind::ResetHorizontal:
reset_shared_range(state, *session);
state->follow_latest = session->data_mode == SessionDataMode::Stream;
state->suppress_range_side_effects = true;
clamp_shared_range(state, *session);
persist_shared_range_to_tab(tab, *state);
layout_changed = true;
success_status = "Horizontal zoom reset";
break;
case PaneMenuActionKind::ResetVertical:
clear_pane_vertical_limits(&tab->panes[static_cast<size_t>(pane_index)]);
layout_changed = true;
success_status = "Vertical zoom reset";
break;
case PaneMenuActionKind::Clear:
clear_pane(tab, pane_index);
tab_state->active_pane_index = pane_index;
+3 -10
View File
@@ -377,20 +377,16 @@ void draw_browser_node(AppSession *session,
}
if (node.children.empty()) {
const bool selected = browser_selection_contains(*state, node.full_path);
const std::string value_text = browser_series_value_text(*session, *state, node.full_path);
const ImGuiStyle &style = ImGui::GetStyle();
const ImVec2 row_size(std::max(1.0f, ImGui::GetContentRegionAvail().x), ImGui::GetFrameHeight());
ImGui::PushID(node.full_path.c_str());
const bool clicked = ImGui::InvisibleButton("##browser_leaf", row_size);
const bool hovered = ImGui::IsItemHovered();
const bool held = ImGui::IsItemActive();
const ImRect rect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
ImDrawList *draw_list = ImGui::GetWindowDrawList();
if (selected || hovered) {
const ImU32 bg = ImGui::GetColorU32(selected
? (held ? ImGuiCol_HeaderActive : ImGuiCol_Header)
: ImGuiCol_HeaderHovered);
if (hovered) {
const ImU32 bg = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
draw_list->AddRectFilled(rect.Min, rect.Max, bg, 0.0f);
}
@@ -409,7 +405,7 @@ void draw_browser_node(AppSession *session,
nullptr);
if (!value_text.empty()) {
app_push_mono_font();
ImGui::PushStyleColor(ImGuiCol_Text, selected ? color_rgb(70, 77, 86) : color_rgb(116, 124, 133));
ImGui::PushStyleColor(ImGuiCol_Text, color_rgb(116, 124, 133));
ImGui::RenderTextClipped(ImVec2(value_left, rect.Min.y + style.FramePadding.y),
ImVec2(value_right, rect.Max.y),
value_text.c_str(),
@@ -452,9 +448,6 @@ void draw_browser_node(AppSession *session,
}
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanAvailWidth;
if (!filter.empty()) {
flags |= ImGuiTreeNodeFlags_DefaultOpen;
}
const bool open = ImGui::TreeNodeEx(node.label.c_str(), flags);
if (open) {
for (const BrowserNode &child : node.children) {
+8
View File
@@ -139,6 +139,14 @@ bool env_flag_enabled(const char *name, bool default_value) {
return !(value == "0" || value == "false" || value == "no" || value == "off");
}
bool app_begin_popup(const char *str_id, ImGuiWindowFlags flags) {
return ImGui::BeginPopup(str_id, flags | ImGuiWindowFlags_NoMove);
}
bool app_begin_popup_modal(const char *name, bool *p_open, ImGuiWindowFlags flags) {
return ImGui::BeginPopupModal(name, p_open, flags | ImGuiWindowFlags_NoMove);
}
void open_external_url(std::string_view url) {
#ifdef __APPLE__
const std::string command = "open " + shell_quote(url) + " &";
+4
View File
@@ -57,6 +57,10 @@ const char *stream_source_kind_label(StreamSourceKind kind);
std::string stream_source_target_label(const StreamSourceConfig &source);
bool env_flag_enabled(const char *name, bool default_value = false);
bool app_begin_popup(const char *str_id, ImGuiWindowFlags flags = 0);
bool app_begin_popup_modal(const char *name,
bool *p_open = nullptr,
ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize);
void open_external_url(std::string_view url);
std::string route_useradmin_url(const RouteIdentifier &route_id);
std::string route_connect_url(const RouteIdentifier &route_id);
+2 -2
View File
@@ -11,7 +11,7 @@
#include <stdexcept>
#include <unistd.h>
#include "third_party/json11/json11.hpp"
#include "json11/json11.hpp"
namespace fs = std::filesystem;
@@ -292,7 +292,7 @@ void draw_custom_series_help_popup(CustomSeriesEditorState *editor) {
ImGui::OpenPopup("Custom Series Help");
editor->open_help = false;
}
if (!ImGui::BeginPopupModal("Custom Series Help", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Custom Series Help")) {
return;
}
ImGui::TextUnformatted("Available variables");
@@ -0,0 +1,327 @@
import sys
import capnp
from pathlib import Path
NO_DISCRIMINANT = 65535
SCALAR_KINDS = {
"bool": "Bool",
"int8": "Int",
"int16": "Int",
"int32": "Int",
"int64": "Int",
"uint8": "UInt",
"uint16": "UInt",
"uint32": "UInt",
"uint64": "UInt",
"float32": "Float",
"float64": "Float",
"enum": "Enum",
}
NESTED_TYPE_KINDS = {"struct", "list"}
IGNORED_TYPE_KINDS = {"void", "text", "data", "interface", "anyPointer"}
def cxx_string(value):
return '"' + value.replace("\\", "\\\\").replace('"', '\\"') + '"'
def accessor(prefix, name):
return prefix + name[:1].upper() + name[1:]
def field_type(field):
if field.proto.which() == "group":
return "struct"
return field.proto.slot.type.which()
def field_type_proto(field):
return field.proto.slot.type if field.proto.which() == "slot" else None
def scalar_kind(type_proto):
if type_proto is None:
return None
return SCALAR_KINDS.get(type_proto.which())
def enum_names(schema):
if schema is None:
return []
names_by_ordinal = schema.enumerants
if not names_by_ordinal:
return []
max_ordinal = max(names_by_ordinal.values())
out = [""] * (max_ordinal + 1)
for name, ordinal in names_by_ordinal.items():
out[ordinal] = name
return out
class Generator:
def __init__(self, event_schema):
self.event_schema = event_schema
self.fixed_paths = []
self.tmp_index = 0
self.lines = []
self.emits_memo = {}
def tmp(self, prefix):
self.tmp_index += 1
return f"{prefix}_{self.tmp_index}"
def add_fixed_path(self, path):
slot = len(self.fixed_paths)
self.fixed_paths.append(path)
return slot
def emit(self, indent, text=""):
self.lines.append(" " * indent + text)
def scalar_double_expr(self, value_expr, kind):
if kind == "Bool":
return f"({value_expr} ? 1.0 : 0.0)"
if kind == "Enum":
return f"static_cast<double>(static_cast<uint16_t>({value_expr}))"
return f"static_cast<double>({value_expr})"
def emit_enum_capture(self, indent, path_expr, names):
if not names:
return
names_expr = "{" + ", ".join(cxx_string(name) for name in names) + "}"
self.emit(indent, f"capture_static_enum_info({path_expr}, {names_expr}, series);")
def emit_node(self, indent, type_kind, type_proto, schema, expr, path, path_expr, dynamic_path):
if not self.node_emits(type_kind, type_proto, schema):
return
kind = scalar_kind(type_proto)
if kind is not None:
double_expr = self.scalar_double_expr(expr, kind)
if dynamic_path:
if kind == "Enum":
self.emit_enum_capture(indent, path_expr, enum_names(schema))
self.emit(indent, f"append_dynamic_scalar_point({path_expr}, tm, {double_expr}, series);")
else:
slot = self.add_fixed_path(path)
if kind == "Enum":
self.emit_enum_capture(indent, cxx_string(path), enum_names(schema))
self.emit(indent, f"append_fixed_scalar_point(&series->fixed_series[{slot}], tm, {double_expr});")
return
if type_kind == "struct":
self.emit_struct(indent, schema, expr, path, path_expr, dynamic_path)
return
if type_kind == "list":
self.emit_list(indent, type_proto, schema, expr, path, path_expr, dynamic_path)
def emit_field(self, indent, struct_schema, reader_expr, field_name, base_path, base_path_expr, dynamic_path):
field = struct_schema.fields[field_name]
proto = field.proto
type_kind = field_type(field)
type_proto = field_type_proto(field)
kind = scalar_kind(type_proto)
value_schema = field.schema if kind == "Enum" or type_kind in NESTED_TYPE_KINDS else None
if not self.node_emits(type_kind, type_proto, value_schema):
return
field_path = f"{base_path}/{field_name}"
field_path_expr = None
if dynamic_path:
field_path_var = self.tmp("path")
self.emit(indent, f"const std::string {field_path_var} = {base_path_expr} + {cxx_string('/' + field_name)};")
field_path_expr = field_path_var
get_call = f"{reader_expr}.{accessor('get', field_name)}()"
has_call = f"{reader_expr}.{accessor('has', field_name)}()"
conditions = []
if proto.discriminantValue != NO_DISCRIMINANT:
conditions.append(f"{reader_expr}.which() == static_cast<decltype({reader_expr}.which())>({proto.discriminantValue})")
if proto.which() == "slot" and type_kind in NESTED_TYPE_KINDS:
conditions.append(has_call)
if conditions:
self.emit(indent, f"if ({' && '.join(conditions)}) {{")
indent += 2
value_var = self.tmp("value")
self.emit(indent, f"const auto {value_var} = {get_call};")
self.emit_node(indent, type_kind, type_proto, value_schema, value_var, field_path, field_path_expr, dynamic_path)
if conditions:
indent -= 2
self.emit(indent, "}")
def emit_struct(self, indent, schema, reader_expr, path, path_expr, dynamic_path):
if schema is None:
return
for field_name in schema.fieldnames:
self.emit_field(indent, schema, reader_expr, field_name, path, path_expr, dynamic_path)
def emit_list(self, indent, type_proto, schema, list_expr, path, path_expr, dynamic_path):
elem_type = type_proto.list.elementType
elem_kind = elem_type.which()
if elem_kind in IGNORED_TYPE_KINDS:
return
base_path_var = path_expr
if base_path_var is None:
base_path_var = self.tmp("base_path")
self.emit(indent, f"const std::string {base_path_var} = {cxx_string(path)};")
elem_scalar = scalar_kind(elem_type)
if elem_scalar is not None:
self.emit(indent, f"if ({list_expr}.size() <= 16) {{")
index_var = self.tmp("i")
self.emit(indent + 2, f"for (uint {index_var} = 0; {index_var} < {list_expr}.size(); ++{index_var}) {{")
item_series = self.tmp("item_series")
self.emit(indent + 4, f"RouteSeries *{item_series} = ensure_list_scalar_series({base_path_var}, {index_var}, series);")
if elem_scalar == "Enum":
self.emit_enum_capture(indent + 4, f"{item_series}->path", enum_names(schema.elementType))
self.emit(indent + 4, f"append_fixed_scalar_point({item_series}, tm, {self.scalar_double_expr(f'{list_expr}[{index_var}]', elem_scalar)});")
self.emit(indent + 2, "}")
self.emit(indent, "}")
return
if elem_kind in {"struct", "list"}:
index_var = self.tmp("i")
self.emit(indent, f"for (uint {index_var} = 0; {index_var} < {list_expr}.size(); ++{index_var}) {{")
item_path = self.tmp("item_path")
self.emit(indent + 2, f"const std::string {item_path} = {base_path_var} + \"/\" + std::to_string({index_var});")
item = self.tmp("item")
self.emit(indent + 2, f"const auto {item} = {list_expr}[{index_var}];")
if elem_kind == "struct":
self.emit_struct(indent + 2, schema.elementType, item, path, item_path, True)
else:
self.emit_list(indent + 2, elem_type, schema.elementType, item, path, item_path, True)
self.emit(indent, "}")
def node_emits(self, type_kind, type_proto, schema, seen=frozenset()):
if scalar_kind(type_proto) is not None:
return True
if type_kind == "struct":
if schema is None:
return False
schema_id = int(schema.node.id)
if schema_id in seen:
return False
if schema_id in self.emits_memo:
return self.emits_memo[schema_id]
next_seen = seen | {schema_id}
for field_name in schema.fieldnames:
field = schema.fields[field_name]
ft = field_type(field)
ftp = field_type_proto(field)
fkind = scalar_kind(ftp)
if ft in IGNORED_TYPE_KINDS:
continue
fschema = field.schema if fkind == "Enum" or ft in NESTED_TYPE_KINDS else None
if self.node_emits(ft, ftp, fschema, next_seen):
self.emits_memo[schema_id] = True
return True
self.emits_memo[schema_id] = False
return False
if type_kind == "list":
if type_proto is None or schema is None:
return False
elem_type = type_proto.list.elementType
elem_kind = elem_type.which()
if elem_kind in IGNORED_TYPE_KINDS:
return False
if scalar_kind(elem_type) is not None:
return True
if elem_kind == "struct":
return self.node_emits("struct", None, schema.elementType, seen)
if elem_kind == "list":
return self.node_emits("list", elem_type, schema.elementType, seen)
return False
def emit_can_special(self, indent, service_name):
service_kind = "CanServiceKind::Can" if service_name == "can" else "CanServiceKind::Sendcan"
self.emit(indent, f"const CanServiceKind can_service = {service_kind};")
self.emit(indent, f"for (const auto &msg : event.{accessor('get', service_name)}()) {{")
self.emit(indent + 2, "append_can_frame(can_service, static_cast<uint8_t>(msg.getSrc()), msg.getAddress(), msg.getDeprecated().getBusTime(), msg.getDat(), tm, series);") # noqa: E501
self.emit(indent + 2, "if (skip_raw_can) {")
self.emit(indent + 4, "const auto dat = msg.getDat();")
self.emit(indent + 4, f"decode_can_frame(can_dbc, {cxx_string(service_name)}, static_cast<uint8_t>(msg.getSrc()), msg.getAddress(), dat.begin(), dat.size(), tm, series);") # noqa: E501
self.emit(indent + 2, "}")
self.emit(indent, "}")
self.emit(indent, "if (skip_raw_can) {")
self.emit(indent + 2, "return true;")
self.emit(indent, "}")
def emit_event_case(self, field_name):
field = self.event_schema.fields[field_name]
proto = field.proto
type_kind = field_type(field)
type_proto = field_type_proto(field)
kind = scalar_kind(type_proto)
schema = field.schema if kind == "Enum" or type_kind in NESTED_TYPE_KINDS else None
self.emit(4, f"case static_cast<cereal::Event::Which>({proto.discriminantValue}): {{")
valid_slot = self.add_fixed_path(f"/{field_name}/valid")
mono_slot = self.add_fixed_path(f"/{field_name}/logMonoTime")
seconds_slot = self.add_fixed_path(f"/{field_name}/t")
self.emit(6, f"append_fixed_scalar_point(&series->fixed_series[{valid_slot}], tm, event.getValid() ? 1.0 : 0.0);")
self.emit(6, f"append_fixed_scalar_point(&series->fixed_series[{mono_slot}], tm, static_cast<double>(event.getLogMonoTime()));")
self.emit(6, f"append_fixed_scalar_point(&series->fixed_series[{seconds_slot}], tm, tm);")
if field_name in {"can", "sendcan"}:
self.emit_can_special(6, field_name)
if self.node_emits(type_kind, type_proto, schema):
payload = self.tmp("payload")
self.emit(6, f"const auto {payload} = event.{accessor('get', field_name)}();")
self.emit_node(6, type_kind, type_proto, schema, payload, f"/{field_name}", None, False)
self.emit(6, "return true;")
self.emit(4, "}")
def generate(self):
self.lines = []
self.emit(0, "// Generated by tools/jotpluggler/generate_event_extractors.py; do not edit.")
self.emit(0, "")
self.emit(0, "const std::vector<std::string> &static_event_fixed_paths() {")
self.emit(2, "static const std::vector<std::string> paths = {")
path_insert_at = len(self.lines)
self.emit(2, "};")
self.emit(2, "return paths;")
self.emit(0, "}")
self.emit(0, "")
self.emit(0, "void capture_static_enum_info(const std::string &path, std::initializer_list<std::string_view> names, SeriesAccumulator *series) {")
self.emit(2, "if (series->enum_info.find(path) != series->enum_info.end()) {")
self.emit(4, "return;")
self.emit(2, "}")
self.emit(2, "EnumInfo info;")
self.emit(2, "info.names.reserve(names.size());")
self.emit(2, "for (std::string_view name : names) {")
self.emit(4, "info.names.emplace_back(name);")
self.emit(2, "}")
self.emit(2, "if (!info.names.empty()) {")
self.emit(4, "series->enum_info.emplace(path, std::move(info));")
self.emit(2, "}")
self.emit(0, "}")
self.emit(0, "")
self.emit(0, "bool append_event_static_reader(cereal::Event::Which which, const cereal::Event::Reader &event, const dbc::Database *can_dbc, bool skip_raw_can, double time_offset, SeriesAccumulator *series) {") # noqa: E501
self.emit(2, "const double tm = static_cast<double>(event.getLogMonoTime()) / 1.0e9 - time_offset;")
self.emit(2, "switch (which) {")
for field_name in self.event_schema.union_fields:
self.emit_event_case(field_name)
self.emit(4, "default:")
self.emit(6, "return false;")
self.emit(2, "}")
self.emit(0, "}")
path_lines = [" " + cxx_string(path) + "," for path in self.fixed_paths]
self.lines[path_insert_at:path_insert_at] = path_lines
return "\n".join(self.lines) + "\n"
if __name__ == "__main__":
if len(sys.argv) != 3:
print(f"usage: {sys.argv[0]} <repo-root> <output>", file=sys.stderr)
sys.exit(2)
repo_root = Path(sys.argv[1]).resolve()
output = Path(sys.argv[2])
capnp.remove_import_hook()
log = capnp.load(str(repo_root / "cereal" / "log.capnp"))
generated = Generator(log.Event.schema).generate()
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(generated)
+1 -1
View File
@@ -4,7 +4,7 @@
#include <cmath>
void icon_add_font(float size, bool merge, const ImFont *base_font) {
const std::filesystem::path ttf = repo_root() / "third_party" / "bootstrap" / "bootstrap-icons.ttf";
const std::filesystem::path ttf = BOOTSTRAP_ICONS_TTF;
ImGuiIO &io = ImGui::GetIO();
ImFontConfig config;
config.MergeMode = merge;
+1 -2
View File
@@ -26,8 +26,6 @@ enum class PaneMenuActionKind {
SplitTop,
SplitBottom,
ResetView,
ResetHorizontal,
ResetVertical,
Clear,
Close,
};
@@ -58,6 +56,7 @@ inline constexpr float SIDEBAR_MAX_WIDTH = 520.0f;
inline constexpr float TIMELINE_BAR_HEIGHT = 14.0f;
inline constexpr float STATUS_BAR_HEIGHT = 52.0f;
inline constexpr double MIN_HORIZONTAL_ZOOM_SECONDS = 2.0;
inline constexpr double PLOT_Y_PADDING_FRACTION = 0.05;
struct UiMetrics {
float width = 0.0f;
+9 -9
View File
@@ -92,7 +92,7 @@ bool open_find_signal_result(UiState *state, const std::string &path) {
}
void draw_open_route_popup(AppSession *session, UiState *state) {
if (!ImGui::BeginPopupModal("Open Route", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Open Route")) {
return;
}
ImGui::TextUnformatted("Load a route into the current layout.");
@@ -116,7 +116,7 @@ void draw_open_route_popup(AppSession *session, UiState *state) {
}
void draw_stream_popup(AppSession *session, UiState *state) {
if (!ImGui::BeginPopupModal("Live Stream", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Live Stream")) {
return;
}
@@ -153,7 +153,7 @@ void draw_stream_popup(AppSession *session, UiState *state) {
}
void draw_load_layout_popup(AppSession *session, UiState *state) {
if (!ImGui::BeginPopupModal("Load Layout", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Load Layout")) {
return;
}
ImGui::TextUnformatted("Load a JotPlugger JSON layout.");
@@ -177,7 +177,7 @@ void draw_load_layout_popup(AppSession *session, UiState *state) {
}
void draw_save_layout_popup(AppSession *session, UiState *state) {
if (!ImGui::BeginPopupModal("Save Layout", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Save Layout")) {
return;
}
ImGui::TextUnformatted("Save the current workspace as a JotPlugger JSON layout.");
@@ -201,7 +201,7 @@ void draw_save_layout_popup(AppSession *session, UiState *state) {
}
void draw_preferences_popup(AppSession *session, UiState *state) {
if (!ImGui::BeginPopupModal("Preferences", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Preferences")) {
return;
}
if (session->map_data) {
@@ -234,7 +234,7 @@ void draw_preferences_popup(AppSession *session, UiState *state) {
}
void draw_find_signal_popup(AppSession *session, UiState *state) {
if (!ImGui::BeginPopupModal("Find Signal", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Find Signal")) {
return;
}
ImGui::TextUnformatted("Search decoded signals across the loaded route.");
@@ -353,7 +353,7 @@ bool save_dbc_editor_contents(AppSession *session, UiState *state) {
}
void draw_dbc_editor_popup(AppSession *session, UiState *state) {
if (!ImGui::BeginPopupModal("DBC Editor", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("DBC Editor")) {
return;
}
DbcEditorState &editor = state->dbc_editor;
@@ -392,7 +392,7 @@ void draw_dbc_editor_popup(AppSession *session, UiState *state) {
}
void draw_axis_limits_popup(AppSession *session, UiState *state) {
if (!ImGui::BeginPopupModal("Edit Axis Limits", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Edit Axis Limits")) {
return;
}
const WorkspaceTab *tab = app_active_tab(session->layout, *state);
@@ -452,7 +452,7 @@ void draw_error_popup(UiState *state) {
ImGui::OpenPopup("Error");
state->open_error_popup = false;
}
if (!ImGui::BeginPopupModal("Error", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (!app_begin_popup_modal("Error")) {
return;
}
ImGui::TextWrapped("%s", state->error_text.c_str());
+1 -1
View File
@@ -6,7 +6,7 @@
#include <sstream>
#include <stdexcept>
#include "third_party/json11/json11.hpp"
#include "json11/json11.hpp"
namespace fs = std::filesystem;
@@ -0,0 +1 @@
{"current_tab_index": 0, "tabs": [{"name": "tab1", "root": {"children": [{"children": [{"camera_view": "driver", "curves": [], "kind": "camera", "title": "Driver Camera"}, {"curves": [{"color": "#236bb4", "name": "/driverMonitoringState/alertLevel"}], "title": "..."}], "sizes": [0.5, 0.5], "split": "vertical"}, {"children": [{"curves": [{"color": "#236bb4", "name": "/driverMonitoringState/activePolicy"}], "title": "..."}, {"curves": [{"color": "#236bb4", "name": "/driverMonitoringState/visionPolicyState/faceDetected"}], "title": "..."}, {"curves": [{"color": "#236bb4", "name": "/driverMonitoringState/visionPolicyState/distractedTypes/eye"}, {"color": "#dc5234", "name": "/driverMonitoringState/visionPolicyState/distractedTypes/phone"}, {"color": "#43a047", "name": "/driverMonitoringState/visionPolicyState/distractedTypes/pose"}], "title": "..."}, {"curves": [{"color": "#236bb4", "name": "/driverMonitoringState/visionPolicyState/awarenessPercent"}], "title": "..."}], "sizes": [0.25, 0.25, 0.25, 0.25], "split": "vertical"}], "sizes": [0.5, 0.5], "split": "horizontal"}}]}
+1 -1
View File
@@ -25,7 +25,7 @@ extern "C" {
#include <vector>
#include "common/util.h"
#include "third_party/json11/json11.hpp"
#include "json11/json11.hpp"
namespace fs = std::filesystem;
+2 -17
View File
@@ -7,8 +7,6 @@
#include <cstdio>
#include <limits>
constexpr double PLOT_Y_PAD_FRACTION = 0.4;
struct PlotBounds {
double x_min = 0.0;
double x_max = 1.0;
@@ -484,7 +482,7 @@ PlotBounds compute_plot_bounds(const Pane &pane,
min_value = std::min(min_value, 0.0);
max_value = std::max(max_value, 1.0);
}
ensure_non_degenerate_range(&min_value, &max_value, PLOT_Y_PAD_FRACTION, 0.1);
ensure_non_degenerate_range(&min_value, &max_value, PLOT_Y_PADDING_FRACTION, 0.1);
if (pane.range.has_y_limit_min) {
min_value = pane.range.y_limit_min;
}
@@ -848,7 +846,7 @@ void draw_plot(const AppSession &session, Pane *pane, UiState *state) {
} else {
for (size_t i = 0; i < prepared_curves.size(); ++i) {
const PreparedCurve &curve = prepared_curves[i];
std::string series_id = curve_legend_label(curve, has_cursor_time, max_legend_label_width) + "##curve" + std::to_string(i);
std::string series_id = curve_legend_label(curve, has_cursor_time, max_legend_label_width) + "###curve" + std::to_string(curve.pane_curve_index);
ImPlotSpec spec;
spec.LineColor = color_rgb(curve.color);
spec.LineWeight = curve.line_weight;
@@ -923,28 +921,15 @@ std::optional<PaneMenuAction> draw_pane_context_menu(const WorkspaceTab &tab, in
ImGui::Separator();
if (icon_menu_item(icon::ZOOM_OUT, "Zoom Out", nullptr, false, is_plot)) {
action.kind = PaneMenuActionKind::ResetView;
} else if (icon_menu_item(icon::ARROW_LEFT_RIGHT, "Zoom Out Horizontally", nullptr, false, is_plot)) {
action.kind = PaneMenuActionKind::ResetHorizontal;
} else if (icon_menu_item(icon::ARROW_DOWN_UP, "Zoom Out Vertically", nullptr, false, is_plot)) {
action.kind = PaneMenuActionKind::ResetVertical;
}
ImGui::Separator();
if (icon_menu_item(icon::TRASH, "Remove ALL curves", nullptr, false, is_plot)) {
action.kind = PaneMenuActionKind::Clear;
}
ImGui::Separator();
icon_menu_item(icon::ARROW_LEFT_RIGHT, "Flip Horizontal Axis", nullptr, false, false);
icon_menu_item(icon::ARROW_DOWN_UP, "Flip Vertical Axis", nullptr, false, false);
ImGui::Separator();
icon_menu_item(icon::FILES, "Copy", nullptr, false, false);
icon_menu_item(icon::CLIPBOARD2, "Paste", nullptr, false, false);
icon_menu_item(icon::FILE_EARMARK_IMAGE, "Copy image to clipboard", nullptr, false, false);
icon_menu_item(icon::SAVE, "Save plot to file", nullptr, false, false);
icon_menu_item(icon::BAR_CHART, "Show data statistics", nullptr, false, false);
ImGui::Separator();
if (icon_menu_item(icon::X_SQUARE, "Close Pane")) {
action.kind = PaneMenuActionKind::Close;
}
ImGui::EndPopup();
if (action.kind == PaneMenuActionKind::None) return std::nullopt;
return action;
-1
View File
@@ -71,7 +71,6 @@ bool should_subscribe_stream_service(const std::string &name) {
"livestreamRoadEncodeIdx",
"livestreamDriverEncodeIdx",
"thumbnail",
"navThumbnail",
}};
if (name == "rawAudioData") return false;
for (std::string_view skipped : kSkippedServices) {
+2 -3
View File
@@ -444,8 +444,7 @@ void draw_route_info_popup(AppSession *session, UiState *state, ImVec2 anchor) {
}
ImGui::SetNextWindowPos(anchor, ImGuiCond_Appearing);
ImGui::SetNextWindowSizeConstraints(ImVec2(300.0f, 0.0f), ImVec2(420.0f, FLT_MAX));
if (!ImGui::BeginPopup("##route_info_popup",
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings)) {
if (!app_begin_popup("##route_info_popup", ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings)) {
return;
}
@@ -643,7 +642,7 @@ void draw_route_id_chip(AppSession *session, UiState *state) {
}
ImGui::SetNextWindowPos(ImVec2(chip_max.x - 60.0f, chip_max.y + 4.0f), ImGuiCond_Appearing);
if (ImGui::BeginPopup("##route_selector_popup")) {
if (app_begin_popup("##route_selector_popup")) {
for (LogSelector selector : {LogSelector::Auto, LogSelector::RLog, LogSelector::QLog}) {
const bool selected = route_id.selector == selector;
const std::string label = std::string(log_selector_name(selector)) + " " + log_selector_description(selector);
+35 -357
View File
@@ -2,7 +2,6 @@
#include "tools/jotpluggler/car_fingerprint_to_dbc.h"
#include "tools/jotpluggler/common.h"
#include <capnp/dynamic.h>
#include <kj/exception.h>
#include <chrono>
@@ -10,6 +9,7 @@
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <initializer_list>
#include <map>
#include <mutex>
#include <limits>
@@ -23,7 +23,7 @@
#include <utility>
#include "common/util.h"
#include "third_party/json11/json11.hpp"
#include "json11/json11.hpp"
#include "tools/replay/logreader.h"
#include "tools/replay/py_downloader.h"
@@ -51,47 +51,7 @@ struct SegmentLogs {
std::string qcamera;
};
enum class ScalarKind {
None,
Bool,
Int,
UInt,
Float,
Enum,
};
enum class ResolvedNodeKind {
Ignore,
Scalar,
Struct,
List,
};
struct ResolvedNode {
ResolvedNodeKind kind = ResolvedNodeKind::Ignore;
ScalarKind scalar_kind = ScalarKind::None;
int fixed_slot = -1;
bool has_field = false;
capnp::StructSchema::Field field;
std::string segment;
std::string path;
bool skip_large_scalar_list = false;
std::vector<ResolvedNode> children;
std::unique_ptr<ResolvedNode> element;
};
struct ResolvedService {
uint16_t event_which = 0;
capnp::StructSchema::Field union_field;
std::string service_name;
int valid_slot = -1;
int log_mono_time_slot = -1;
int seconds_slot = -1;
ResolvedNode payload;
};
struct SchemaIndex {
std::vector<std::optional<ResolvedService>> by_which;
size_t fixed_series_count = 0;
std::vector<std::string> fixed_paths;
@@ -112,6 +72,27 @@ struct SeriesAccumulator {
std::unordered_map<std::string, EnumInfo> enum_info;
};
void append_fixed_scalar_point(RouteSeries *series, double tm, double value);
void append_dynamic_scalar_point(const std::string &path, double tm, double value, SeriesAccumulator *series);
RouteSeries *ensure_list_scalar_series(const std::string &base_path, size_t index, SeriesAccumulator *series);
void append_can_frame(CanServiceKind service,
uint8_t bus,
uint32_t address,
uint16_t bus_time,
capnp::Data::Reader dat,
double tm,
SeriesAccumulator *series);
void decode_can_frame(const dbc::Database *can_dbc,
const std::string &service_name,
uint8_t bus,
uint32_t address,
const uint8_t *raw,
size_t data_size,
double tm,
SeriesAccumulator *series);
#include "tools/jotpluggler/generated_event_extractors.h"
struct LoadedRouteArtifacts {
std::vector<RouteSeries> series;
std::vector<CanMessageData> can_messages;
@@ -898,125 +879,11 @@ SketchLayout parse_layout(const fs::path &layout_path) {
return layout;
}
ScalarKind scalar_kind_for_type(const capnp::Type &type) {
if (type.isBool()) return ScalarKind::Bool;
if (type.isInt8() || type.isInt16() || type.isInt32() || type.isInt64()) {
return ScalarKind::Int;
}
if (type.isUInt8() || type.isUInt16() || type.isUInt32() || type.isUInt64()) {
return ScalarKind::UInt;
}
if (type.isFloat32() || type.isFloat64()) {
return ScalarKind::Float;
}
if (type.isEnum()) return ScalarKind::Enum;
return ScalarKind::None;
}
ResolvedNode build_resolved_type(const capnp::Type &type,
bool has_field,
capnp::StructSchema::Field field,
std::string segment,
std::string path,
size_t *next_fixed_slot,
std::vector<std::string> *fixed_paths,
bool dynamic_path = false) {
ResolvedNode node;
node.has_field = has_field;
node.field = field;
node.segment = std::move(segment);
node.path = std::move(path);
node.scalar_kind = scalar_kind_for_type(type);
if (node.scalar_kind != ScalarKind::None) {
node.kind = ResolvedNodeKind::Scalar;
if (!dynamic_path) {
node.fixed_slot = static_cast<int>((*next_fixed_slot)++);
fixed_paths->push_back(node.path);
}
return node;
}
if (type.isStruct()) {
node.kind = ResolvedNodeKind::Struct;
for (auto child : type.asStruct().getFields()) {
const std::string child_segment = child.getProto().getName().cStr();
node.children.push_back(build_resolved_type(
child.getType(),
true,
child,
child_segment,
node.path + "/" + child_segment,
next_fixed_slot,
fixed_paths,
dynamic_path));
}
return node;
}
if (type.isList()) {
const capnp::Type element_type = type.asList().getElementType();
if (element_type.isText() || element_type.isData() || element_type.isInterface() || element_type.isAnyPointer()) {
node.kind = ResolvedNodeKind::Ignore;
return node;
}
node.kind = ResolvedNodeKind::List;
node.skip_large_scalar_list = scalar_kind_for_type(element_type) != ScalarKind::None;
node.element = std::make_unique<ResolvedNode>(
build_resolved_type(element_type,
false,
capnp::StructSchema::Field(),
"",
node.path,
next_fixed_slot,
fixed_paths,
true));
return node;
}
node.kind = ResolvedNodeKind::Ignore;
return node;
}
int register_fixed_series_path(const std::string &path,
size_t *next_fixed_slot,
std::vector<std::string> *fixed_paths) {
const int slot = static_cast<int>((*next_fixed_slot)++);
fixed_paths->push_back(path);
return slot;
}
const SchemaIndex &SchemaIndex::instance() {
static const SchemaIndex index = [] {
SchemaIndex out;
const auto event_schema = capnp::Schema::from<cereal::Event>().asStruct();
uint16_t max_discriminant = 0;
for (auto union_field : event_schema.getUnionFields()) {
max_discriminant = std::max<uint16_t>(max_discriminant, union_field.getProto().getDiscriminantValue());
}
out.by_which.resize(static_cast<size_t>(max_discriminant) + 1);
size_t next_fixed_slot = 0;
for (auto union_field : event_schema.getUnionFields()) {
ResolvedService service;
service.event_which = union_field.getProto().getDiscriminantValue();
service.union_field = union_field;
service.service_name = union_field.getProto().getName().cStr();
service.valid_slot = register_fixed_series_path(
"/" + service.service_name + "/valid", &next_fixed_slot, &out.fixed_paths);
service.log_mono_time_slot = register_fixed_series_path(
"/" + service.service_name + "/logMonoTime", &next_fixed_slot, &out.fixed_paths);
service.seconds_slot = register_fixed_series_path(
"/" + service.service_name + "/t", &next_fixed_slot, &out.fixed_paths);
service.payload = build_resolved_type(
union_field.getType(),
false,
capnp::StructSchema::Field(),
service.service_name,
"/" + service.service_name,
&next_fixed_slot,
&out.fixed_paths);
out.by_which[service.event_which] = std::move(service);
}
out.fixed_series_count = next_fixed_slot;
out.fixed_paths = static_event_fixed_paths();
out.fixed_series_count = out.fixed_paths.size();
return out;
}();
return index;
@@ -1026,45 +893,6 @@ bool is_absolute_curve(const std::string &name) {
return !name.empty() && name.front() == '/';
}
std::optional<double> scalar_value_to_double(const capnp::DynamicValue::Reader &value, ScalarKind kind) {
switch (kind) {
case ScalarKind::Bool:
return value.as<bool>() ? 1.0 : 0.0;
case ScalarKind::Int:
return static_cast<double>(value.as<int64_t>());
case ScalarKind::UInt:
return static_cast<double>(value.as<uint64_t>());
case ScalarKind::Float:
return value.as<double>();
case ScalarKind::Enum:
return static_cast<double>(value.as<capnp::DynamicEnum>().getRaw());
case ScalarKind::None:
return std::nullopt;
}
return std::nullopt;
}
void capture_enum_info(const std::string &path,
const capnp::DynamicValue::Reader &value,
SeriesAccumulator *series) {
if (series->enum_info.find(path) != series->enum_info.end()) {
return;
}
const auto dynamic_enum = value.as<capnp::DynamicEnum>();
EnumInfo info;
for (auto enumerant : dynamic_enum.getSchema().getEnumerants()) {
const uint16_t ordinal = enumerant.getOrdinal();
if (ordinal >= info.names.size()) {
info.names.resize(static_cast<size_t>(ordinal) + 1);
}
info.names[ordinal] = enumerant.getProto().getName().cStr();
}
if (!info.names.empty()) {
series->enum_info.emplace(path, std::move(info));
}
}
void append_scalar_point(RouteSeries *series,
const std::string &path,
double tm,
@@ -1195,156 +1023,9 @@ void append_dynamic_scalar_point(const std::string &path, double tm, double valu
append_scalar_point(ensure_dynamic_series(path, series), path, tm, value);
}
void append_scalar_value(const ResolvedNode &node,
const std::string *path_override,
const capnp::DynamicValue::Reader &raw_value,
double tm,
double value,
SeriesAccumulator *series) {
if (path_override == nullptr && node.fixed_slot >= 0) {
if (node.scalar_kind == ScalarKind::Enum) {
capture_enum_info(node.path, raw_value, series);
}
append_fixed_scalar_point(&series->fixed_series[static_cast<size_t>(node.fixed_slot)], tm, value);
return;
}
const std::string &path = path_override != nullptr ? *path_override : node.path;
if (node.scalar_kind == ScalarKind::Enum) {
capture_enum_info(path, raw_value, series);
}
append_dynamic_scalar_point(path, tm, value, series);
}
void append_fast_node(const ResolvedNode &node,
const capnp::DynamicValue::Reader &value,
double tm,
SeriesAccumulator *series,
const std::string *path_override = nullptr) {
switch (node.kind) {
case ResolvedNodeKind::Scalar: {
if (std::optional<double> scalar = scalar_value_to_double(value, node.scalar_kind); scalar.has_value()) {
append_scalar_value(node, path_override, value, tm, *scalar, series);
}
return;
}
case ResolvedNodeKind::Struct: {
const capnp::DynamicStruct::Reader reader = value.as<capnp::DynamicStruct>();
for (const ResolvedNode &child : node.children) {
if (!child.has_field || !reader.has(child.field)) continue;
if (path_override == nullptr) {
append_fast_node(child, reader.get(child.field), tm, series, nullptr);
} else {
const std::string child_path = child.segment.empty() ? *path_override : (*path_override + "/" + child.segment);
append_fast_node(child, reader.get(child.field), tm, series, &child_path);
}
}
return;
}
case ResolvedNodeKind::List: {
if (!node.element) {
return;
}
const capnp::DynamicList::Reader list = value.as<capnp::DynamicList>();
if (list.size() == 0) {
return;
}
if (node.skip_large_scalar_list && list.size() > 16) {
return;
}
const std::string &base_path = path_override != nullptr ? *path_override : node.path;
if (node.element->kind == ResolvedNodeKind::Scalar) {
for (uint i = 0; i < list.size(); ++i) {
if (std::optional<double> scalar = scalar_value_to_double(list[i], node.element->scalar_kind); scalar.has_value()) {
RouteSeries *item_series = ensure_list_scalar_series(base_path, i, series);
if (node.element->scalar_kind == ScalarKind::Enum && !item_series->path.empty()) {
capture_enum_info(item_series->path, list[i], series);
}
append_fixed_scalar_point(item_series, tm, *scalar);
}
}
return;
}
for (uint i = 0; i < list.size(); ++i) {
const std::string item_path = base_path + "/" + std::to_string(i);
append_fast_node(*node.element, list[i], tm, series, &item_path);
}
return;
}
case ResolvedNodeKind::Ignore:
return;
}
}
void append_event_fast_reader(cereal::Event::Which which,
const cereal::Event::Reader &event,
const SchemaIndex &schema,
const dbc::Database *can_dbc,
bool skip_raw_can,
double time_offset,
SeriesAccumulator *series) {
const uint16_t which_index = static_cast<uint16_t>(which);
if (which_index >= schema.by_which.size() || !schema.by_which[which_index].has_value()) {
return;
}
const ResolvedService &service = *schema.by_which[which_index];
const capnp::DynamicStruct::Reader dynamic_event(event);
const capnp::DynamicValue::Reader payload = dynamic_event.get(service.union_field);
const double tm = static_cast<double>(event.getLogMonoTime()) / 1.0e9 - time_offset;
append_fixed_scalar_point(&series->fixed_series[static_cast<size_t>(service.valid_slot)],
tm,
event.getValid() ? 1.0 : 0.0);
append_fixed_scalar_point(&series->fixed_series[static_cast<size_t>(service.log_mono_time_slot)],
tm,
static_cast<double>(event.getLogMonoTime()));
append_fixed_scalar_point(&series->fixed_series[static_cast<size_t>(service.seconds_slot)],
tm,
tm);
if (service.service_name == "can" || service.service_name == "sendcan") {
const CanServiceKind can_service = service.service_name == "can"
? CanServiceKind::Can
: CanServiceKind::Sendcan;
auto decode_message = [&](uint8_t bus, uint32_t address, const auto &dat_reader) {
const auto bytes = dat_reader.begin();
decode_can_frame(can_dbc, service.service_name, bus, address, bytes, dat_reader.size(), tm, series);
};
if (service.service_name == "can") {
for (const auto &msg : event.getCan()) {
append_can_frame(can_service,
static_cast<uint8_t>(msg.getSrc()),
msg.getAddress(),
msg.getDeprecated().getBusTime(),
msg.getDat(),
tm,
series);
if (!skip_raw_can) continue;
decode_message(static_cast<uint8_t>(msg.getSrc()), msg.getAddress(), msg.getDat());
}
} else {
for (const auto &msg : event.getSendcan()) {
append_can_frame(can_service,
static_cast<uint8_t>(msg.getSrc()),
msg.getAddress(),
msg.getDeprecated().getBusTime(),
msg.getDat(),
tm,
series);
if (!skip_raw_can) continue;
decode_message(static_cast<uint8_t>(msg.getSrc()), msg.getAddress(), msg.getDat());
}
}
if (skip_raw_can) {
return;
}
}
append_fast_node(service.payload, payload, tm, series);
}
void append_event_fast(cereal::Event::Which which,
int32_t eidx_segnum,
kj::ArrayPtr<const capnp::word> data,
const SchemaIndex &schema,
const dbc::Database *can_dbc,
bool skip_raw_can,
double time_offset,
@@ -1353,14 +1034,13 @@ void append_event_fast(cereal::Event::Which which,
return;
}
with_parseable_event(data, [&](const cereal::Event::Reader &event) {
append_event_fast_reader(which, event, schema, can_dbc, skip_raw_can, time_offset, series);
append_event_static_reader(which, event, can_dbc, skip_raw_can, time_offset, series);
});
}
void append_events_fast_range(const std::vector<Event> &events,
size_t begin,
size_t end,
const SchemaIndex &schema,
const dbc::Database *can_dbc,
bool skip_raw_can,
SeriesAccumulator *series) {
@@ -1369,7 +1049,6 @@ void append_events_fast_range(const std::vector<Event> &events,
append_event_fast(event_record.which,
event_record.eidx_segnum,
event_record.data,
schema,
can_dbc,
skip_raw_can,
0.0,
@@ -1802,7 +1481,7 @@ SeriesAccumulator extract_segment_series(const std::vector<Event> &events,
const size_t chunk_count = extract_chunk_count(events.size(), worker_budget, segment_workers);
if (chunk_count <= 1 || events.empty()) {
SeriesAccumulator series = make_series_accumulator(schema);
append_events_fast_range(events, 0, events.size(), schema, can_dbc, skip_raw_can, &series);
append_events_fast_range(events, 0, events.size(), can_dbc, skip_raw_can, &series);
return series;
}
@@ -1819,10 +1498,10 @@ SeriesAccumulator extract_segment_series(const std::vector<Event> &events,
workers.emplace_back([&, chunk]() {
const size_t begin = chunk * events_per_chunk;
const size_t end = std::min(events.size(), begin + events_per_chunk);
append_events_fast_range(events, begin, end, schema, can_dbc, skip_raw_can, &chunk_results[chunk]);
append_events_fast_range(events, begin, end, can_dbc, skip_raw_can, &chunk_results[chunk]);
});
}
append_events_fast_range(events, 0, std::min(events.size(), events_per_chunk), schema, can_dbc, skip_raw_can, &chunk_results[0]);
append_events_fast_range(events, 0, std::min(events.size(), events_per_chunk), can_dbc, skip_raw_can, &chunk_results[0]);
for (std::thread &worker : workers) {
worker.join();
}
@@ -2044,13 +1723,12 @@ void StreamAccumulator::appendEvent(kj::ArrayPtr<const capnp::word> data) {
}
}
append_event_fast_reader(which,
event,
impl_->schema,
impl_->can_dbc ? &*impl_->can_dbc : nullptr,
impl_->can_dbc.has_value(),
*impl_->time_offset,
&impl_->series);
append_event_static_reader(which,
event,
impl_->can_dbc ? &*impl_->can_dbc : nullptr,
impl_->can_dbc.has_value(),
*impl_->time_offset,
&impl_->series);
append_log_event(which, event, *impl_->time_offset, &impl_->logs, &impl_->last_alert_key);
if (which == cereal::Event::Which::SELFDRIVE_STATE) {
const auto sd = event.getSelfdriveState();
+1 -1
View File
@@ -110,7 +110,7 @@ def send_thread(joystick):
def joystick_control_thread(joystick):
Params().put_bool('JoystickDebugMode', True)
Params().put_bool('JoystickDebugMode', True, block=True)
threading.Thread(target=send_thread, args=(joystick,), daemon=True).start()
while True:
joystick.update()
@@ -13,7 +13,7 @@ from openpilot.tools.longitudinal_maneuvers.maneuversd import Action, Maneuver a
# thresholds for starting maneuvers
MAX_SPEED_DEV = 0.7 # deviation in m/s
MAX_CURV = 0.002 # 500 m radius
MAX_ROLL = 0.08 # 4.56°
MAX_ROLL = 0.12 # 6.8°
TIMER = 2.0 # sec stable conditions before starting maneuver
@dataclass
+8 -2
View File
@@ -21,8 +21,14 @@ if [ "$(uname)" == "Darwin" ] && [ $SHELL == "/bin/bash" ]; then
fi
function op_install() {
echo "Installing op system-wide..."
CMD="\nalias op='"$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )/op.sh" \"\$@\"'\n"
grep "alias op=" "$RC_FILE" &> /dev/null || printf "$CMD" >> $RC_FILE
OP_SH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )/op.sh"
CMD=$(cat <<EOF
alias op='$OP_SH "\$@"'
_op_completions() { [ "\$COMP_CWORD" -eq 1 ] && COMPREPLY=(\$(compgen -W "\$(awk '/shift 1; op_/{print \$1}' $OP_SH)" -- "\${COMP_WORDS[1]}")); }
[ -n "\$BASH_VERSION" ] && complete -F _op_completions -o default op
EOF
)
grep -q "alias op=" "$RC_FILE" 2>/dev/null || printf '\n%s\n' "$CMD" >> "$RC_FILE"
echo -e " ↳ [${GREEN}${NC}] op installed successfully. Open a new shell to use it."
}
+17
View File
@@ -29,6 +29,18 @@ MINIMUM_PLOTJUGGLER_VERSION = (3, 5, 2)
MAX_STREAMING_BUFFER_SIZE = 1000
def print_jotpluggler_banner():
purple = "\033[95m" if sys.stdout.isatty() else ""
reset = "\033[0m" if purple else ""
print(f"{purple}+-------------------------------------------------------------+{reset}")
print(f"{purple}|{reset} JotPluggler is the future! Try it like this: {purple}|{reset}")
print(f"{purple}|{reset} ./tools/jotpluggler/jotpluggler --demo --layout tuning {purple}|{reset}")
print(f"{purple}|{reset} {purple}|{reset}")
print(f"{purple}|{reset} PlotJuggler will be deleted soon. {purple}|{reset}")
print(f"{purple}|{reset} Missing a feature? Open an issue or post in #dev-openpilot. {purple}|{reset}")
print(f"{purple}+-------------------------------------------------------------+{reset}")
def install():
m = f"{platform.system()}-{platform.machine()}"
supported = ("Linux-x86_64", "Linux-aarch64", "Darwin-arm64")
@@ -118,10 +130,15 @@ if __name__ == "__main__":
parser.add_argument("route_or_segment_name", nargs='?', help="The route or segment name to plot (cabana share URL accepted)")
if len(sys.argv) == 1:
print_jotpluggler_banner()
print()
parser.print_help()
sys.exit()
args = parser.parse_args()
print_jotpluggler_banner()
print()
if args.install:
install()
sys.exit()
+15 -58
View File
@@ -5,6 +5,7 @@
#include <mutex>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include "tools/replay/util.h"
@@ -13,9 +14,9 @@ namespace {
static std::mutex handler_mutex;
static DownloadProgressHandler progress_handler = nullptr;
// Run a Python command and capture stdout. Optionally parse stderr for PROGRESS lines.
// Run a Python command and capture stdout. Stderr is left attached to the parent.
// Returns stdout content. If abort is signaled, kills the child process.
std::string runPython(const std::vector<std::string> &args, std::atomic<bool> *abort = nullptr, bool parse_progress = false) {
std::string runPython(const std::vector<std::string> &args, std::atomic<bool> *abort = nullptr) {
// Build argv for execvp
std::vector<const char *> argv;
argv.push_back("python3");
@@ -27,22 +28,15 @@ std::string runPython(const std::vector<std::string> &args, std::atomic<bool> *a
argv.push_back(nullptr);
int stdout_pipe[2];
int stderr_pipe[2];
if (pipe(stdout_pipe) != 0) {
rWarning("py_downloader: pipe() failed");
return {};
}
if (pipe(stderr_pipe) != 0) {
rWarning("py_downloader: pipe() failed");
close(stdout_pipe[0]); close(stdout_pipe[1]);
return {};
}
pid_t pid = fork();
if (pid < 0) {
rWarning("py_downloader: fork() failed");
close(stdout_pipe[0]); close(stdout_pipe[1]);
close(stderr_pipe[0]); close(stderr_pipe[1]);
return {};
}
@@ -61,11 +55,8 @@ std::string runPython(const std::vector<std::string> &args, std::atomic<bool> *a
unsetenv("OPENPILOT_PREFIX");
close(stdout_pipe[0]);
close(stderr_pipe[0]);
dup2(stdout_pipe[1], STDOUT_FILENO);
dup2(stderr_pipe[1], STDERR_FILENO);
close(stdout_pipe[1]);
close(stderr_pipe[1]);
execvp("python3", const_cast<char *const *>(argv.data()));
_exit(127);
@@ -73,32 +64,28 @@ std::string runPython(const std::vector<std::string> &args, std::atomic<bool> *a
// Parent process
close(stdout_pipe[1]);
close(stderr_pipe[1]);
std::string stdout_data;
std::string stderr_buf;
char buf[4096];
// Use select() to read from both pipes
// Use select() so abort can interrupt while waiting for Python output.
fd_set rfds;
int max_fd = std::max(stdout_pipe[0], stderr_pipe[0]);
bool stdout_open = true, stderr_open = true;
bool stdout_open = true;
while (stdout_open || stderr_open) {
while (stdout_open) {
if (abort && *abort) {
kill(pid, SIGTERM);
break;
}
FD_ZERO(&rfds);
if (stdout_open) FD_SET(stdout_pipe[0], &rfds);
if (stderr_open) FD_SET(stderr_pipe[0], &rfds);
FD_SET(stdout_pipe[0], &rfds);
struct timeval tv = {0, 100000}; // 100ms timeout
int ret = select(max_fd + 1, &rfds, nullptr, nullptr, &tv);
int ret = select(stdout_pipe[0] + 1, &rfds, nullptr, nullptr, &tv);
if (ret < 0) break;
if (stdout_open && FD_ISSET(stdout_pipe[0], &rfds)) {
if (FD_ISSET(stdout_pipe[0], &rfds)) {
ssize_t n = read(stdout_pipe[0], buf, sizeof(buf));
if (n <= 0) {
stdout_open = false;
@@ -106,45 +93,15 @@ std::string runPython(const std::vector<std::string> &args, std::atomic<bool> *a
stdout_data.append(buf, n);
}
}
if (stderr_open && FD_ISSET(stderr_pipe[0], &rfds)) {
ssize_t n = read(stderr_pipe[0], buf, sizeof(buf));
if (n <= 0) {
stderr_open = false;
} else {
stderr_buf.append(buf, n);
// Parse complete lines from stderr
size_t pos;
while ((pos = stderr_buf.find('\n')) != std::string::npos) {
std::string line = stderr_buf.substr(0, pos);
stderr_buf.erase(0, pos + 1);
if (parse_progress && line.rfind("PROGRESS:", 0) == 0) {
// Parse "PROGRESS:<cur>:<total>"
auto colon1 = line.find(':', 9);
if (colon1 != std::string::npos) {
try {
uint64_t cur = std::stoull(line.c_str() + 9);
uint64_t total = std::stoull(line.c_str() + colon1 + 1);
std::lock_guard<std::mutex> lk(handler_mutex);
if (progress_handler) {
progress_handler(cur, total, true);
}
} catch (...) {}
}
} else if (line.rfind("ERROR:", 0) == 0) {
rWarning("py_downloader: %s", line.c_str() + 6);
}
}
}
}
}
// Drain remaining pipe data to prevent child from blocking on write
for (int fd : {stdout_pipe[0], stderr_pipe[0]}) {
while (read(fd, buf, sizeof(buf)) > 0) {}
close(fd);
while (true) {
ssize_t n = read(stdout_pipe[0], buf, sizeof(buf));
if (n <= 0) break;
stdout_data.append(buf, n);
}
close(stdout_pipe[0]);
int status;
waitpid(pid, &status, 0);
@@ -192,7 +149,7 @@ std::string download(const std::string &url, bool use_cache, std::atomic<bool> *
if (!use_cache) {
args.push_back("--no-cache");
}
return runPython(args, abort, true);
return runPython(args, abort);
}
std::string getRouteFiles(const std::string &route) {
+2 -2
View File
@@ -1,7 +1,7 @@
#include "qcom_decoder.h"
#include <assert.h>
#include "third_party/linux/include/v4l2-controls.h"
#include <linux/v4l2-controls.h>
#include <linux/videodev2.h>
@@ -343,4 +343,4 @@ bool MsmVidc::handleEvent() {
break;
}
return true;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
#include <filesystem>
#include <regex>
#include "third_party/json11/json11.hpp"
#include "json11/json11.hpp"
#include "system/hardware/hw.h"
#include "tools/replay/py_downloader.h"
#include "tools/replay/replay.h"
+1 -1
View File
@@ -8,7 +8,7 @@
#include <thread>
#include <vector>
#include "third_party/json11/json11.hpp"
#include "json11/json11.hpp"
#include "tools/replay/framereader.h"
#include "tools/replay/logreader.h"
#include "tools/replay/util.h"
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
import argparse
import shlex
import subprocess
import threading
import time
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
from openpilot.common.basedir import BASEDIR
def build_rsync_cmd(args) -> list[str]:
ssh = [
"ssh",
"-o", "ControlMaster=auto",
"-o", f"ControlPath=/tmp/devsync-{args.ip}.ctl",
"-o", "ControlPersist=10m",
"-o", "StrictHostKeyChecking=accept-new",
]
if args.identity:
ssh += ["-i", args.identity]
return [
"rsync", "-az",
"--files-from=-", "--from0",
"-e", " ".join(shlex.quote(p) for p in ssh),
"--out-format=%n",
BASEDIR + "/", f"comma@{args.ip}:{args.remote}/",
]
def git_tracked_files() -> bytes:
return subprocess.check_output(
["git", "-C", BASEDIR, "ls-files", "--recurse-submodules", "-z"]
)
class Handler(FileSystemEventHandler):
def __init__(self, sync_fn):
self.dirty = threading.Event()
self.sync_fn = sync_fn
def on_any_event(self, event):
if not event.is_directory:
self.dirty.set()
def run(self):
while True:
time.sleep(1)
if self.dirty.is_set():
self.dirty.clear()
self.sync_fn()
def main():
p = argparse.ArgumentParser()
p.add_argument("ip", help="device IP / hostname")
p.add_argument("--remote", default="/data/openpilot", help="remote path on device")
p.add_argument("-i", "--identity", default=None, help="ssh identity file")
args = p.parse_args()
print(f"[devsync] watching {BASEDIR}")
print(f"[devsync] target comma@{args.ip}:{args.remote}")
def run_sync():
file_list = git_tracked_files()
cmd = build_rsync_cmd(args)
t0 = time.monotonic()
r = subprocess.run(cmd, input=file_list, capture_output=True)
dt = time.monotonic() - t0
if r.returncode:
print(f"[devsync] ERR rc={r.returncode} in {dt:.2f}s")
return
files = [ln for ln in r.stdout.decode().splitlines() if ln.strip()]
msg = f"{len(files)} files: {', '.join(files)}" if files else "no changes"
print(f"[devsync] {dt:.2f}s · {msg}")
run_sync()
handler = Handler(run_sync)
obs = Observer()
obs.schedule(handler, BASEDIR, recursive=True)
obs.start()
handler.run()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[devsync] stopping")
+3 -3
View File
@@ -15,9 +15,9 @@ if __name__ == "__main__":
if keys.status_code == 200:
params = Params()
params.put_bool("SshEnabled", True)
params.put("GithubSshKeys", keys.text)
params.put("GithubUsername", username)
params.put_bool("SshEnabled", True, block=True)
params.put("GithubSshKeys", keys.text, block=True)
params.put("GithubUsername", username, block=True)
print("Set up ssh keys successfully")
else:
print("Error getting public keys from github")
+48 -49
View File
@@ -19,7 +19,7 @@ function retry() {
return 1
}
function install_ubuntu_deps() {
function install_linux_deps() {
SUDO=""
if [[ ! $(id -u) -eq 0 ]]; then
@@ -30,61 +30,60 @@ function install_ubuntu_deps() {
SUDO="sudo"
fi
# Detect OS using /etc/os-release file
if [ -f "/etc/os-release" ]; then
source /etc/os-release
case "$VERSION_CODENAME" in
"jammy" | "kinetic" | "noble")
;;
*)
echo "$ID $VERSION_ID is unsupported. This setup script is written for Ubuntu 24.04."
read -p "Would you like to attempt installation anyway? " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
;;
esac
local missing_linux_deps=0
for cmd in gcc g++ make curl curl-config git; do
if ! command -v "$cmd" > /dev/null 2>&1; then
missing_linux_deps=1
break
fi
done
# normal stuff, this mostly for bare docker images
if [[ "$missing_linux_deps" -eq 0 ]]; then
# the native package managers are slow, so skip if we can
echo "[ ] system packages already installed t=$SECONDS"
elif command -v apt-get > /dev/null 2>&1; then
$SUDO apt-get update
$SUDO apt-get install -y --no-install-recommends ca-certificates build-essential curl libcurl4-openssl-dev locales git
elif command -v dnf > /dev/null 2>&1; then
$SUDO dnf install -y ca-certificates gcc gcc-c++ make curl libcurl-devel glibc-langpack-en git
elif command -v yum > /dev/null 2>&1; then
$SUDO yum install -y ca-certificates gcc gcc-c++ make curl libcurl-devel glibc-langpack-en git
elif command -v pacman > /dev/null 2>&1; then
$SUDO pacman -Syu --noconfirm --needed base-devel ca-certificates curl git
elif command -v zypper > /dev/null 2>&1; then
$SUDO zypper --non-interactive refresh
$SUDO zypper --non-interactive install ca-certificates gcc gcc-c++ make curl libcurl-devel glibc-locale git
elif command -v apk > /dev/null 2>&1; then
$SUDO apk add --no-cache ca-certificates build-base curl curl-dev musl-locales git
elif command -v xbps-install > /dev/null 2>&1; then
$SUDO xbps-install -Syu base-devel ca-certificates curl git libcurl-devel glibc-locales
else
echo "No /etc/os-release in the system. Make sure you're running on Ubuntu, or similar."
echo "Unsupported Linux distribution. Supported package managers: apt-get, dnf, yum, pacman, zypper, apk, xbps-install."
exit 1
fi
$SUDO apt-get update
# normal stuff, mostly for the bare docker image
$SUDO apt-get install -y --no-install-recommends \
ca-certificates \
build-essential \
curl \
libcurl4-openssl-dev \
locales \
git \
xvfb
if [[ -d "/etc/udev/rules.d/" ]]; then
# Setup jungle udev rules
$SUDO tee /etc/udev/rules.d/12-panda_jungle.rules > /dev/null <<EOF
SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddcf", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddef", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddcf", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddef", MODE="0666"
$SUDO tee /etc/udev/rules.d/11-openpilot.rules > /dev/null <<-EOF
# Panda Jungle devices
SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddcf", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddef", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddcf", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddef", MODE="0666"
EOF
# Panda devices
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddcc", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddee", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddcc", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddee", MODE="0666"
# Setup panda udev rules
$SUDO tee /etc/udev/rules.d/11-panda.rules > /dev/null <<EOF
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddcc", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="3801", ATTRS{idProduct}=="ddee", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddcc", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="bbaa", ATTRS{idProduct}=="ddee", MODE="0666"
EOF
# comma devices over ADB
SUBSYSTEM=="usb", ATTR{idVendor}=="04d8", ATTR{idProduct}=="1234", ENV{adb_user}="yes"
EOF
# Setup adb udev rules
$SUDO tee /etc/udev/rules.d/50-comma-adb.rules > /dev/null <<EOF
SUBSYSTEM=="usb", ATTR{idVendor}=="04d8", ATTR{idProduct}=="1234", ENV{adb_user}="yes"
EOF
# delete the old ones
$SUDO rm -f /etc/udev/rules.d/11-panda.rules /etc/udev/rules.d/12-panda_jungle.rules /etc/udev/rules.d/50-comma-adb.rules
$SUDO udevadm control --reload-rules && $SUDO udevadm trigger || true
fi
@@ -116,7 +115,7 @@ function install_python_deps() {
# --- Main ---
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
install_ubuntu_deps
install_linux_deps
echo "[ ] installed system dependencies t=$SECONDS"
elif [[ "$OSTYPE" == "darwin"* ]]; then
if [[ $SHELL == "/bin/zsh" ]]; then
+1 -1
View File
@@ -40,7 +40,7 @@ class SimulatorBridge(ABC):
def __init__(self, dual_camera, high_quality):
set_params_enabled()
self.params = Params()
self.params.put_bool("AlphaLongitudinalEnabled", True)
self.params.put_bool("AlphaLongitudinalEnabled", True, block=True)
self.rk = Ratekeeper(100, None)
+1 -1
View File
@@ -84,7 +84,7 @@ class SimulatedCar:
if self.params.get_bool("ObdMultiplexingEnabled") != self.obd_multiplexing:
self.obd_multiplexing = not self.obd_multiplexing
self.params.put_bool("ObdMultiplexingChanged", True)
self.params.put_bool("ObdMultiplexingChanged", True, block=True)
dat = messaging.new_message('pandaStates', 1)
dat.valid = True
+6 -10
View File
@@ -23,17 +23,12 @@ class SimulatedSensors:
def send_imu_message(self, simulator_state: 'SimulatorState'):
for _ in range(5):
dat = messaging.new_message('accelerometer', valid=True)
dat.accelerometer.sensor = 4
dat.accelerometer.type = 0x10
dat.accelerometer.timestamp = dat.logMonoTime # TODO: use the IMU timestamp
dat.accelerometer.init('acceleration')
dat.accelerometer.acceleration.v = [simulator_state.imu.accelerometer.x, simulator_state.imu.accelerometer.y, simulator_state.imu.accelerometer.z]
self.pm.send('accelerometer', dat)
# copied these numbers from locationd
dat = messaging.new_message('gyroscope', valid=True)
dat.gyroscope.sensor = 5
dat.gyroscope.type = 0x10
dat.gyroscope.timestamp = dat.logMonoTime # TODO: use the IMU timestamp
dat.gyroscope.init('gyroUncalibrated')
dat.gyroscope.gyroUncalibrated.v = [simulator_state.imu.gyroscope.x, simulator_state.imu.gyroscope.y, simulator_state.imu.gyroscope.z]
@@ -92,11 +87,12 @@ class SimulatedSensors:
# dmonitoringd output
dat = messaging.new_message('driverMonitoringState', valid=True)
dat.driverMonitoringState = {
"faceDetected": True,
"isDistracted": False,
"awarenessStatus": 1.,
}
dm = dat.driverMonitoringState
dm.alertLevel = log.DriverMonitoringState.AlertLevel.none
dm.activePolicy = log.DriverMonitoringState.MonitoringPolicy.vision
dm.visionPolicyState.faceDetected = True
dm.visionPolicyState.isDistracted = False
dm.visionPolicyState.awarenessPercent = 100
self.pm.send('driverMonitoringState', dat)
def send_camera_images(self, world: 'World'):