mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-09-14 19:43:50 +08:00
Merge branch 'upstream/master' into sync-20260517-new-new
This commit is contained in:
+1
-1
@@ -29,7 +29,7 @@ source .venv/bin/activate
|
||||
|
||||
**4. Build openpilot**
|
||||
``` bash
|
||||
scons -u -j$(nproc)
|
||||
scons -u
|
||||
```
|
||||
|
||||
## WSL on Windows
|
||||
|
||||
@@ -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,5 +1,6 @@
|
||||
moc_*
|
||||
*.moc
|
||||
*.generated.qrc
|
||||
|
||||
assets.cc
|
||||
|
||||
|
||||
+13
-1
@@ -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,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>
|
||||
|
||||
@@ -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"> x </span>,
|
||||
<span style="background-color:lightGray;color:gray"> Backspace </span>,
|
||||
<span style="background-color:lightGray;color:gray"> Delete </span><br />
|
||||
Change endianness: <span style="background-color:lightGray;color:gray"> e </span><br />
|
||||
Change singedness: <span style="background-color:lightGray;color:gray"> s </span><br />
|
||||
Change signedness: <span style="background-color:lightGray;color:gray"> s </span><br />
|
||||
Open chart:
|
||||
<span style="background-color:lightGray;color:gray"> c </span>,
|
||||
<span style="background-color:lightGray;color:gray"> p </span>,
|
||||
|
||||
+1
-1
@@ -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" "$@"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 -->
|
||||
)"));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) + " &";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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"}}]}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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."
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
Executable
+93
@@ -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")
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'):
|
||||
|
||||
Reference in New Issue
Block a user