Record feedback with LKAS button (#35888)

* record feedback with LKAS button

* fix alert test

* slightly simplify feedbackd

* "Audio Feedback Saved" upon time expiration or early stop

* earlySend --> earlyStop

* userFlag --> userBookmark

* RecordAudioFeedback param/toggle

* add audioFeedback test

* simplify feedbackd

* send bookmark regardless of toggle, show feedback event with higher priority

* add userBookmark to selfdrived sm

* fix mispelled param name

* default off and move to main

* segmentNum --> blockNum, earlyStop --> lastBlock

* preserve audioFeedback

* get rid of lastBlock and just send bookmark saved at the end

* update raylib side

* update toggle description and add raylib toggle

---------

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
pull/35925/head
Jimmy 2 days ago committed by GitHub
parent 112d615ac9
commit d7b0a5fa7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 16
      cereal/log.capnp
  2. 2
      cereal/messaging/tests/test_pub_sub_master.py
  3. 4
      cereal/services.py
  4. 1
      common/params_keys.h
  5. 18
      selfdrive/selfdrived/events.py
  6. 11
      selfdrive/selfdrived/selfdrived.py
  7. 8
      selfdrive/test/process_replay/process_replay.py
  8. 1
      selfdrive/test/test_onroad.py
  9. 70
      selfdrive/ui/feedback/feedbackd.py
  10. 12
      selfdrive/ui/layouts/main.py
  11. 10
      selfdrive/ui/layouts/settings/toggles.py
  12. 7
      selfdrive/ui/qt/offroad/settings.cc
  13. 6
      selfdrive/ui/qt/sidebar.cc
  14. 52
      selfdrive/ui/tests/test_feedbackd.py
  15. 10
      system/loggerd/loggerd.cc
  16. 8
      system/loggerd/tests/test_loggerd.py
  17. 1
      system/manager/process_config.py
  18. 4
      tools/cabana/videowidget.cc
  19. 2
      tools/replay/README.md
  20. 4
      tools/replay/consoleui.cc
  21. 2
      tools/replay/main.cc
  22. 2
      tools/replay/replay.cc
  23. 6
      tools/replay/timeline.cc
  24. 4
      tools/replay/timeline.h

@ -127,8 +127,9 @@ struct OnroadEvent @0xc4fa6047f024e718 {
espActive @90;
personalityChanged @91;
aeb @92;
userFlag @95;
userBookmark @95;
excessiveActuation @96;
audioFeedback @97;
soundsUnavailableDEPRECATED @47;
}
@ -2468,7 +2469,7 @@ struct DebugAlert {
alertText2 @1 :Text;
}
struct UserFlag {
struct UserBookmark @0xfe346a9de48d9b50 {
}
struct SoundPressure @0xdc24138990726023 {
@ -2486,6 +2487,11 @@ struct AudioData {
sampleRate @1 :UInt32;
}
struct AudioFeedback {
audio @0 :AudioData;
blockNum @1 :UInt16;
}
struct Touch {
sec @0 :Int64;
usec @1 :Int64;
@ -2586,9 +2592,13 @@ struct Event {
mapRenderState @105: MapRenderState;
# UI services
userFlag @93 :UserFlag;
uiDebug @102 :UIDebug;
# driving feedback
userBookmark @93 :UserBookmark;
bookmarkButton @148 :UserBookmark;
audioFeedback @149 :AudioFeedback;
# *********** debug ***********
testJoystick @52 :Joystick;
roadEncodeData @86 :EncodeData;

@ -86,7 +86,7 @@ class TestSubMaster:
"cameraOdometry": (20, 10),
"liveCalibration": (4, 4),
"carParams": (None, None),
"userFlag": (None, None),
"userBookmark": (None, None),
}
for service, (max_freq, min_freq) in checks.items():

@ -72,9 +72,11 @@ _services: dict[str, tuple] = {
"navRoute": (True, 0.),
"navThumbnail": (True, 0.),
"qRoadEncodeIdx": (False, 20.),
"userFlag": (True, 0., 1),
"userBookmark": (True, 0., 1),
"soundPressure": (True, 10., 10),
"rawAudioData": (False, 20.),
"bookmarkButton": (True, 0., 1),
"audioFeedback": (True, 0., 1),
# debug
"uiDebug": (True, 0., 1),

@ -105,6 +105,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}},
{"PrimeType", {PERSISTENT, INT}},
{"RecordAudio", {PERSISTENT, BOOL}},
{"RecordAudioFeedback", {PERSISTENT, BOOL, "0"}},
{"RecordFront", {PERSISTENT, BOOL}},
{"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet
{"SecOCKey", {PERSISTENT | DONT_LOG, STRING}},

@ -11,6 +11,8 @@ from openpilot.common.constants import CV
from openpilot.common.git import get_short_branch
from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.locationd.calibrationd import MIN_SPEED_FILTER
from openpilot.system.micd import SAMPLE_RATE, SAMPLE_BUFFER
from openpilot.selfdrive.ui.feedback.feedbackd import FEEDBACK_MAX_DURATION
AlertSize = log.SelfdriveState.AlertSize
AlertStatus = log.SelfdriveState.AlertStatus
@ -198,6 +200,7 @@ class StartupAlert(Alert):
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 5.),
# ********** helper functions **********
def get_display_speed(speed_ms: float, metric: bool) -> str:
speed = int(round(speed_ms * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH)))
@ -252,6 +255,14 @@ def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messag
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2)
def audio_feedback_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
duration = FEEDBACK_MAX_DURATION - ((sm['audioFeedback'].blockNum + 1) * SAMPLE_BUFFER / SAMPLE_RATE)
return NormalPermanentAlert(
"Recording Audio Feedback",
f"{round(duration)} second{'s' if round(duration) != 1 else ''} remaining. Press again to save early.",
priority=Priority.LOW)
# *** debug alerts ***
def out_of_space_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
@ -370,6 +381,7 @@ def invalid_lkas_setting_alert(CP: car.CarParams, CS: car.CarState, sm: messagin
return NormalPermanentAlert("Invalid LKAS setting", text)
EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
# ********** events with no alerts **********
@ -991,9 +1003,13 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
ET.WARNING: personality_changed_alert,
},
EventName.userFlag: {
EventName.userBookmark: {
ET.PERMANENT: NormalPermanentAlert("Bookmark Saved", duration=1.5),
},
EventName.audioFeedback: {
ET.PERMANENT: audio_feedback_alert,
},
}

@ -95,7 +95,7 @@ class SelfdriveD:
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', 'liveDelay',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userFlag'] + \
'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userBookmark', 'audioFeedback'] + \
self.camera_packets + self.sensor_packets + self.gps_packets,
ignore_alive=ignore, ignore_avg_freq=ignore,
ignore_valid=ignore, frequency=int(1/DT_CTRL))
@ -181,9 +181,12 @@ class SelfdriveD:
self.events.add(EventName.selfdriveInitializing)
return
# Check for user flag (bookmark) press
if self.sm.updated['userFlag']:
self.events.add(EventName.userFlag)
# Check for user bookmark press (bookmark button or end of LKAS button feedback)
if self.sm.updated['userBookmark']:
self.events.add(EventName.userBookmark)
if self.sm.updated['audioFeedback']:
self.events.add(EventName.audioFeedback)
# Don't add any more events while in dashcam mode
if self.CP.passive:

@ -418,10 +418,10 @@ CONFIGS = [
proc_name="selfdrived",
pubs=[
"carState", "deviceState", "pandaStates", "peripheralState", "liveCalibration", "driverMonitoringState",
"longitudinalPlan", "livePose", "liveDelay", "liveParameters", "radarState",
"modelV2", "driverCameraState", "roadCameraState", "wideRoadCameraState", "managerState",
"liveTorqueParameters", "accelerometer", "gyroscope", "carOutput",
"gpsLocationExternal", "gpsLocation", "controlsState", "carControl", "driverAssistance", "alertDebug",
"longitudinalPlan", "livePose", "liveDelay", "liveParameters", "radarState", "modelV2",
"driverCameraState", "roadCameraState", "wideRoadCameraState", "managerState", "liveTorqueParameters",
"accelerometer", "gyroscope", "carOutput", "gpsLocationExternal", "gpsLocation", "controlsState",
"carControl", "driverAssistance", "alertDebug", "audioFeedback",
],
subs=["selfdriveState", "onroadEvents"],
ignore=["logMonoTime"],

@ -55,6 +55,7 @@ PROCS = {
"selfdrive.locationd.paramsd": 9.0,
"selfdrive.locationd.lagd": 11.0,
"selfdrive.ui.soundd": 3.0,
"selfdrive.ui.feedback.feedbackd": 1.0,
"selfdrive.monitoring.dmonitoringd": 4.0,
"./proclogd": 2.0,
"system.logmessaged": 1.0,

@ -0,0 +1,70 @@
#!/usr/bin/env python3
import cereal.messaging as messaging
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from cereal import car
from openpilot.system.micd import SAMPLE_RATE, SAMPLE_BUFFER
FEEDBACK_MAX_DURATION = 10.0
ButtonType = car.CarState.ButtonEvent.Type
def main():
params = Params()
pm = messaging.PubMaster(['userBookmark', 'audioFeedback'])
sm = messaging.SubMaster(['rawAudioData', 'bookmarkButton', 'carState'])
should_record_audio = False
block_num = 0
waiting_for_release = False
early_stop_triggered = False
while True:
sm.update()
should_send_bookmark = False
if sm.updated['carState'] and sm['carState'].canValid:
for be in sm['carState'].buttonEvents:
if be.type == ButtonType.lkas:
if be.pressed:
if not should_record_audio:
if params.get_bool("RecordAudioFeedback"): # Start recording on first press if toggle set
should_record_audio = True
block_num = 0
waiting_for_release = False
early_stop_triggered = False
cloudlog.info("LKAS button pressed - starting 10-second audio feedback")
else:
should_send_bookmark = True # immediately send bookmark if toggle false
cloudlog.info("LKAS button pressed - bookmarking")
elif should_record_audio and not waiting_for_release: # Wait for release of second press to stop recording early
waiting_for_release = True
elif waiting_for_release: # Second press released
waiting_for_release = False
early_stop_triggered = True
cloudlog.info("LKAS button released - ending recording early")
if should_record_audio and sm.updated['rawAudioData']:
raw_audio = sm['rawAudioData']
msg = messaging.new_message('audioFeedback', valid=True)
msg.audioFeedback.audio.data = raw_audio.data
msg.audioFeedback.audio.sampleRate = raw_audio.sampleRate
msg.audioFeedback.blockNum = block_num
block_num += 1
if (block_num * SAMPLE_BUFFER / SAMPLE_RATE) >= FEEDBACK_MAX_DURATION or early_stop_triggered: # Check for timeout or early stop
should_send_bookmark = True # send bookmark at end of audio segment
should_record_audio = False
early_stop_triggered = False
cloudlog.info("10-second recording completed or second button press - stopping audio feedback")
pm.send('audioFeedback', msg)
if sm.updated['bookmarkButton']:
cloudlog.info("Bookmark button pressed!")
should_send_bookmark = True
if should_send_bookmark:
msg = messaging.new_message('userBookmark', valid=True)
pm.send('userBookmark', msg)
if __name__ == '__main__':
main()

@ -19,7 +19,7 @@ class MainLayout(Widget):
def __init__(self):
super().__init__()
self._pm = messaging.PubMaster(['userFlag'])
self._pm = messaging.PubMaster(['bookmarkButton'])
self._sidebar = Sidebar()
self._current_mode = MainState.HOME
@ -40,7 +40,7 @@ class MainLayout(Widget):
def _setup_callbacks(self):
self._sidebar.set_callbacks(on_settings=self._on_settings_clicked,
on_flag=self._on_flag_clicked)
on_flag=self._on_bookmark_clicked)
self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE))
self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state)
self._layouts[MainState.ONROAD].set_callbacks(on_click=self._on_onroad_clicked)
@ -76,10 +76,10 @@ class MainLayout(Widget):
def _on_settings_clicked(self):
self.open_settings(PanelType.DEVICE)
def _on_flag_clicked(self):
user_flag = messaging.new_message('userFlag')
user_flag.valid = True
self._pm.send('userFlag', user_flag)
def _on_bookmark_clicked(self):
user_bookmark = messaging.new_message('bookmarkButton')
user_bookmark.valid = True
self._pm.send('bookmarkButton', user_bookmark)
def _on_onroad_clicked(self):
self._sidebar.set_visible(not self._sidebar.is_visible)

@ -23,6 +23,10 @@ DESCRIPTIONS = {
'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.",
"IsMetric": "Display speed in km/h instead of mph.",
"RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.",
"RecordAudioFeedback": (
"Press the LKAS button to record audio feedback about openpilot. When this toggle is disabled, the button acts as a bookmark button. " +
"The event will be highlighted in comma connect and the segment will be preserved on your device's storage."
),
}
@ -81,6 +85,12 @@ class TogglesLayout(Widget):
self._params.get_bool("RecordAudio"),
icon="microphone.png",
),
toggle_item(
"Record Audio Feedback with LKAS button",
DESCRIPTIONS["RecordAudioFeedback"],
self._params.get_bool("RecordAudioFeedback"),
icon="microphone.png",
),
toggle_item(
"Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="metric.png"
),

@ -68,6 +68,13 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
"../assets/icons/microphone.png",
true,
},
{
"RecordAudioFeedback",
tr("Record Audio Feedback with LKAS button"),
tr("Press the LKAS button to record audio feedback about openpilot. When this toggle is disabled, the button acts as a bookmark button. The event will be highlighted in comma connect and the segment will be preserved on your device's storage."),
"../assets/icons/microphone.png",
false,
},
{
"IsMetric",
tr("Use Metric System"),

@ -38,7 +38,7 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
pm = std::make_unique<PubMaster>(std::vector<const char*>{"userFlag"});
pm = std::make_unique<PubMaster>(std::vector<const char*>{"bookmarkButton"});
}
void Sidebar::mousePressEvent(QMouseEvent *event) {
@ -61,8 +61,8 @@ void Sidebar::mouseReleaseEvent(QMouseEvent *event) {
}
if (onroad && home_btn.contains(event->pos())) {
MessageBuilder msg;
msg.initEvent().initUserFlag();
pm->send("userFlag", msg);
msg.initEvent().initBookmarkButton();
pm->send("bookmarkButton", msg);
} else if (settings_btn.contains(event->pos())) {
emit openSettings();
} else if (recording_audio && mic_indicator_btn.contains(event->pos())) {

@ -0,0 +1,52 @@
import pytest
import cereal.messaging as messaging
from cereal import car
from openpilot.common.params import Params
from openpilot.system.manager.process_config import managed_processes
class TestFeedbackd:
def setup_method(self):
self.pm = messaging.PubMaster(['carState', 'rawAudioData'])
self.sm = messaging.SubMaster(['audioFeedback'])
def _send_lkas_button(self, pressed: bool):
msg = messaging.new_message('carState')
msg.carState.canValid = True
msg.carState.buttonEvents = [{'type': car.CarState.ButtonEvent.Type.lkas, 'pressed': pressed}]
self.pm.send('carState', msg)
def _send_audio_data(self, count: int = 5):
for _ in range(count):
audio_msg = messaging.new_message('rawAudioData')
audio_msg.rawAudioData.data = bytes(1600) # 800 samples of int16
audio_msg.rawAudioData.sampleRate = 16000
self.pm.send('rawAudioData', audio_msg)
self.sm.update(timeout=100)
@pytest.mark.parametrize("record_feedback", [False, True])
def test_audio_feedback(self, record_feedback):
Params().put_bool("RecordAudioFeedback", record_feedback)
managed_processes["feedbackd"].start()
assert self.pm.wait_for_readers_to_update('carState', timeout=5)
assert self.pm.wait_for_readers_to_update('rawAudioData', timeout=5)
self._send_lkas_button(pressed=True)
self._send_audio_data()
self._send_lkas_button(pressed=False)
self._send_audio_data()
if record_feedback:
assert self.sm.updated['audioFeedback'], "audioFeedback should be published when enabled"
else:
assert not self.sm.updated['audioFeedback'], "audioFeedback should not be published when disabled"
self._send_lkas_button(pressed=True)
self._send_audio_data()
self._send_lkas_button(pressed=False)
self._send_audio_data()
assert not self.sm.updated['audioFeedback'], "audioFeedback should not be published after second press"
managed_processes["feedbackd"].stop()

@ -194,7 +194,7 @@ int handle_encoder_msg(LoggerdState *s, Message *msg, std::string &name, struct
return bytes_count;
}
void handle_user_flag(LoggerdState *s) {
void handle_preserve_segment(LoggerdState *s) {
static int prev_segment = -1;
if (s->logger.segment() == prev_segment) return;
@ -222,7 +222,7 @@ void loggerd_thread() {
typedef struct ServiceState {
std::string name;
int counter, freq;
bool encoder, user_flag, record_audio;
bool encoder, preserve_segment, record_audio;
} ServiceState;
std::unordered_map<SubSocket*, ServiceState> service_state;
std::unordered_map<SubSocket*, struct RemoteEncoder> remote_encoders;
@ -246,7 +246,7 @@ void loggerd_thread() {
.counter = 0,
.freq = it.decimation,
.encoder = encoder,
.user_flag = it.name == "userFlag",
.preserve_segment = (it.name == "userBookmark") || (it.name == "audioFeedback"),
.record_audio = record_audio,
};
}
@ -281,8 +281,8 @@ void loggerd_thread() {
if (do_exit) break;
ServiceState &service = service_state[sock];
if (service.user_flag) {
handle_user_flag(&s);
if (service.preserve_segment) {
handle_preserve_segment(&s);
}
// drain socket

@ -282,15 +282,15 @@ class TestLoggerd:
sent.clear_write_flag()
assert sent.to_bytes() == m.as_builder().to_bytes()
def test_preserving_flagged_segments(self):
services = set(random.sample(CEREAL_SERVICES, random.randint(5, 10))) | {"userFlag"}
def test_preserving_bookmarked_segments(self):
services = set(random.sample(CEREAL_SERVICES, random.randint(5, 10))) | {"userBookmark"}
self._publish_random_messages(services)
segment_dir = self._get_latest_log_dir()
assert getxattr(segment_dir, PRESERVE_ATTR_NAME) == PRESERVE_ATTR_VALUE
def test_not_preserving_unflagged_segments(self):
services = set(random.sample(CEREAL_SERVICES, random.randint(5, 10))) - {"userFlag"}
def test_not_preserving_nonbookmarked_segments(self):
services = set(random.sample(CEREAL_SERVICES, random.randint(5, 10))) - {"userBookmark", "audioFeedback"}
self._publish_random_messages(services)
segment_dir = self._get_latest_log_dir()

@ -106,6 +106,7 @@ procs = [
PythonProcess("updated", "system.updated.updated", only_offroad, enabled=not PC),
PythonProcess("uploader", "system.loggerd.uploader", always_run),
PythonProcess("statsd", "system.statsd", always_run),
PythonProcess("feedbackd", "selfdrive.ui.feedback.feedbackd", only_onroad),
# debug procs
NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar),

@ -19,7 +19,7 @@ const int THUMBNAIL_MARGIN = 3;
static const QColor timeline_colors[] = {
[(int)TimelineType::None] = QColor(111, 143, 175),
[(int)TimelineType::Engaged] = QColor(0, 163, 108),
[(int)TimelineType::UserFlag] = Qt::magenta,
[(int)TimelineType::UserBookmark] = Qt::magenta,
[(int)TimelineType::AlertInfo] = Qt::green,
[(int)TimelineType::AlertWarning] = QColor(255, 195, 0),
[(int)TimelineType::AlertCritical] = QColor(199, 0, 57),
@ -64,7 +64,7 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) {
Pause/Resume: <span style="background-color:lightGray;color:gray">&nbsp;space&nbsp;</span>
)").arg(timeline_colors[(int)TimelineType::None].name(),
timeline_colors[(int)TimelineType::Engaged].name(),
timeline_colors[(int)TimelineType::UserFlag].name(),
timeline_colors[(int)TimelineType::UserBookmark].name(),
timeline_colors[(int)TimelineType::AlertInfo].name(),
timeline_colors[(int)TimelineType::AlertWarning].name(),
timeline_colors[(int)TimelineType::AlertCritical].name()));

@ -75,7 +75,7 @@ Options:
--qcam load qcamera
--no-hw-decoder disable HW video decoding
--no-vipc do not output video
--all do output all messages including uiDebug, userFlag.
--all do output all messages including uiDebug, userBookmark.
this may causes issues when used along with UI
Arguments:

@ -257,7 +257,7 @@ void ConsoleUI::updateTimeline() {
if (entry.type == TimelineType::Engaged) {
mvwchgat(win, 1, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL);
mvwchgat(win, 2, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL);
} else if (entry.type == TimelineType::UserFlag) {
} else if (entry.type == TimelineType::UserBookmark) {
mvwchgat(win, 3, start_pos, end_pos - start_pos + 1, ACS_S3, Color::Cyan, NULL);
} else {
auto color_id = Color::Green;
@ -329,7 +329,7 @@ void ConsoleUI::handleKey(char c) {
} else if (c == 'd') {
replay->seekToFlag(FindFlag::nextDisEngagement);
} else if (c == 't') {
replay->seekToFlag(FindFlag::nextUserFlag);
replay->seekToFlag(FindFlag::nextUserBookmark);
} else if (c == 'i') {
replay->seekToFlag(FindFlag::nextInfo);
} else if (c == 'w') {

@ -30,7 +30,7 @@ Options:
--qcam Load qcamera
--no-hw-decoder Disable HW video decoding
--no-vipc Do not output video
--all Output all messages including uiDebug, userFlag
--all Output all messages including uiDebug, userBookmark
-h, --help Show this help message
)";

@ -20,7 +20,7 @@ Replay::Replay(const std::string &route, std::vector<std::string> allow, std::ve
std::signal(SIGUSR1, interrupt_sleep_handler);
if (!(flags_ & REPLAY_FLAG_ALL_SERVICES)) {
block.insert(block.end(), {"uiDebug", "userFlag"});
block.insert(block.end(), {"uiDebug", "userBookmark"});
}
setupServices(allow, block);
setupSegmentManager(!allow.empty() || !block.empty());

@ -26,7 +26,7 @@ std::optional<uint64_t> Timeline::find(double cur_ts, FindFlag flag) const {
return entry.end_time;
}
} else if (entry.start_time > cur_ts) {
if ((flag == FindFlag::nextUserFlag && entry.type == TimelineType::UserFlag) ||
if ((flag == FindFlag::nextUserBookmark && entry.type == TimelineType::UserBookmark) ||
(flag == FindFlag::nextInfo && entry.type == TimelineType::AlertInfo) ||
(flag == FindFlag::nextWarning && entry.type == TimelineType::AlertWarning) ||
(flag == FindFlag::nextCritical && entry.type == TimelineType::AlertCritical)) {
@ -66,8 +66,8 @@ void Timeline::buildTimeline(const Route &route, uint64_t route_start_ts, bool l
auto cs = reader.getRoot<cereal::Event>().getSelfdriveState();
updateEngagementStatus(cs, current_engaged_idx, seconds);
updateAlertStatus(cs, current_alert_idx, seconds);
} else if (e.which == cereal::Event::Which::USER_FLAG) {
staging_entries_.emplace_back(Entry{seconds, seconds, TimelineType::UserFlag});
} else if (e.which == cereal::Event::Which::USER_BOOKMARK) {
staging_entries_.emplace_back(Entry{seconds, seconds, TimelineType::UserBookmark});
}
}

@ -7,8 +7,8 @@
#include "tools/replay/route.h"
enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag };
enum class FindFlag { nextEngagement, nextDisEngagement, nextUserFlag, nextInfo, nextWarning, nextCritical };
enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserBookmark };
enum class FindFlag { nextEngagement, nextDisEngagement, nextUserBookmark, nextInfo, nextWarning, nextCritical };
class Timeline {
public:

Loading…
Cancel
Save