Merge remote-tracking branch 'sunnyhaibin/openpilot-1/palisade-2023-port-1' into palisade-2023-hda2-long

# Conflicts:
#	selfdrive/car/hyundai/hyundaican.py
#	selfdrive/car/hyundai/interface.py
#	selfdrive/car/hyundai/values.py
This commit is contained in:
Jason Wen
2024-04-26 14:52:08 -04:00
27 changed files with 437 additions and 319 deletions
+3 -1
View File
@@ -1,8 +1,10 @@
Version 0.9.7 (2024-XX-XX)
Version 0.9.7 (2024-05-XX)
========================
* New driving model
* Adjust driving personality with the follow distance button
* Support for hybrid variants of supported Ford models
* Added toggle to enable driver monitoring even when openpilot is not engaged
* Fingerprinting without the OBD-II port on all cars
* Hyundai Palisade (with HDA II) 2023-24 support thanks to sunnyhaibin!
* Kia Telluride (with HDA II) 2023-24 support thanks to sunnyhaibin!
+3 -3
View File
@@ -104,10 +104,10 @@ class CarController(CarControllerBase):
can_sends.append([0x7b1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", self.CAN.ECAN])
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
can_canfd = self.CP.flags & HyundaiFlags.CAN_CANFD
can_canfd_hybrid = self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID
# CAN-FD platforms
if self.CP.carFingerprint in CANFD_CAR or (hda2 and can_canfd):
if self.CP.carFingerprint in CANFD_CAR or (hda2 and can_canfd_hybrid):
hda2_long = hda2 and self.CP.openpilotLongitudinalControl
# steering control
@@ -146,7 +146,7 @@ class CarController(CarControllerBase):
self.accel_last = accel
else:
# button presses
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=(hda2 and can_canfd)))
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=(hda2 and can_canfd_hybrid)))
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_steer, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
+15 -15
View File
@@ -8,7 +8,7 @@ from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \
CANFD_CAR, Buttons, CarControllerParams
CANFD_CAR, Buttons, CarControllerParams, CAN_CANFD_HYBRID_CAR
from openpilot.selfdrive.car.interfaces import CarStateBase
PREV_BUTTON_SAMPLES = 8
@@ -27,7 +27,7 @@ class CarState(CarStateBase):
self.gear_msg_canfd = "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
"GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \
"GEAR_SHIFTER"
if CP.carFingerprint in CANFD_CAR:
if CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
self.shifter_values = can_define.dv[self.gear_msg_canfd]["GEAR"]
elif self.CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"]
@@ -53,7 +53,7 @@ class CarState(CarStateBase):
self.params = CarControllerParams(CP)
def update(self, cp, cp_cam):
if self.CP.carFingerprint in CANFD_CAR:
if self.CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
return self.update_canfd(cp, cp_cam)
ret = car.CarState.new_message()
@@ -107,7 +107,7 @@ class CarState(CarStateBase):
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
else:
scc_bus = "SCC12" if self.CP.flags & HyundaiFlags.CAN_CANFD else "SCC11"
scc_bus = "SCC12" if self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else "SCC11"
ret.cruiseState.available = cp_cruise.vl[scc_bus]["MainMode_ACC"] == 1
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
ret.cruiseState.standstill = cp_cruise.vl[scc_bus]["SCCInfoDisplay"] == 4.
@@ -145,7 +145,7 @@ class CarState(CarStateBase):
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if not self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.CAN_CANFD.value):
if not self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID):
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
@@ -159,10 +159,10 @@ class CarState(CarStateBase):
ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0
# save the entire LKAS11 and CLU11
if not self.CP.flags & HyundaiFlags.CAN_CANFD:
if not self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID:
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
self.clu11 = copy.copy(cp.vl["CLU11"])
if self.CP.flags & HyundaiFlags.CAN_CANFD and self.CP.flags & HyundaiFlags.CANFD_HDA2:
if self.CP.flags & HyundaiFlags.CAN_CANFD_HYBRID and self.CP.flags & HyundaiFlags.CANFD_HDA2:
self.hda2_lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x2a4"])
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
self.prev_cruise_buttons = self.cruise_buttons[-1]
@@ -254,12 +254,12 @@ class CarState(CarStateBase):
return ret
def get_can_parser(self, CP):
if CP.carFingerprint in CANFD_CAR:
if CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
return self.get_can_parser_canfd(CP)
messages = [
# address, frequency
("MDPS12", 100 if CP.flags & HyundaiFlags.CAN_CANFD else 50),
("MDPS12", 100 if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 50),
("TCS11", 100),
("TCS13", 50),
("TCS15", 10),
@@ -274,7 +274,7 @@ class CarState(CarStateBase):
]
if not CP.openpilotLongitudinalControl:
if CP.flags & HyundaiFlags.CAN_CANFD:
if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID:
messages.append(("SCC12", 50))
elif CP.carFingerprint not in CAMERA_SCC_CAR:
messages += [
@@ -285,7 +285,7 @@ class CarState(CarStateBase):
messages.append(("FCA11", 50))
if CP.enableBsm:
messages.append(("LCA11", 20 if CP.flags & HyundaiFlags.CAN_CANFD else 50))
messages.append(("LCA11", 20 if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 50))
if CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV):
messages.append(("E_EMS11", 50))
@@ -304,17 +304,17 @@ class CarState(CarStateBase):
else:
messages.append(("LVR12", 100))
bus = CanBus(CP).ECAN if CP.flags & HyundaiFlags.CAN_CANFD.value else 0
bus = CanBus(CP).ECAN if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 0
return CANParser(DBC[CP.carFingerprint]["pt"], messages, bus)
@staticmethod
def get_cam_can_parser(CP):
if CP.carFingerprint in CANFD_CAR:
if CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
return CarState.get_cam_can_parser_canfd(CP)
messages = []
if CP.flags & HyundaiFlags.CAN_CANFD:
if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID:
messages.append(("CAM_0x2a4", 20))
else:
messages.append(("LKAS11", 100))
@@ -328,7 +328,7 @@ class CarState(CarStateBase):
if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))
bus = CanBus(CP).CAM if CP.flags & HyundaiFlags.CAN_CANFD else 2
bus = CanBus(CP).CAM if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 2
return CANParser(DBC[CP.carFingerprint]["pt"], messages, bus)
def get_can_parser_canfd(self, CP):
+1 -1
View File
@@ -113,7 +113,7 @@ def create_clu11(packer, frame, clu11, button, CP, CAN):
values["CF_Clu_CruiseSwState"] = button
values["CF_Clu_AliveCnt1"] = frame % 0x10
# send buttons to camera on camera-scc based cars
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else CAN.ECAN if CP.flags & HyundaiFlags.CAN_CANFD else 0
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else CAN.ECAN if CP.flags & HyundaiFlags.CAN_CANFD_HYBRID else 0
return packer.make_can_msg("CLU11", bus, values)
+22 -25
View File
@@ -3,7 +3,7 @@ from panda import Panda
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, Buttons, CAN_CANFD_CAR
UNSUPPORTED_LONGITUDINAL_CAR, Buttons, CAN_CANFD_HYBRID_CAR
from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
from openpilot.selfdrive.car import create_button_events, get_safety_config
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
@@ -36,14 +36,13 @@ class CarInterface(CarInterfaceBase):
if hda2:
ret.flags |= HyundaiFlags.CANFD_HDA2.value
if candidate in CANFD_CAR:
if candidate in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
# detect if car is hybrid
if 0x105 in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlags.HYBRID.value
elif candidate in EV_CAR:
ret.flags |= HyundaiFlags.EV.value
# detect HDA2 with ADAS Driving ECU
if hda2:
if 0x110 in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
@@ -66,10 +65,6 @@ class CarInterface(CarInterfaceBase):
elif candidate in EV_CAR:
ret.flags |= HyundaiFlags.EV.value
# detect cars with hybrid definitions of CAN and CAN-FD
if candidate in CAN_CANFD_CAR:
ret.flags |= HyundaiFlags.CAN_CANFD.value
# Send LFA message on cars with HDA
if 0x485 in fingerprint[2]:
ret.flags |= HyundaiFlags.SEND_LFA.value
@@ -92,7 +87,7 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(platform_str, ret.lateralTuning)
# *** longitudinal control ***
if candidate in CANFD_CAR:
if candidate in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
ret.longitudinalTuning.kpV = [0.1]
ret.longitudinalTuning.kiV = [0.0]
ret.experimentalLongitudinalAvailable = candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR)
@@ -114,45 +109,43 @@ class CarInterface(CarInterfaceBase):
ret.stoppingDecelRate = 0.4
# *** feature detection ***
if candidate in CANFD_CAR:
if candidate in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
ret.enableBsm = 0x1e5 in fingerprint[CAN.ECAN]
else:
bus = CAN.ECAN if ret.flags & HyundaiFlags.CAN_CANFD else 0
bus = CAN.ECAN if ret.flags & HyundaiFlags.CAN_CANFD_HYBRID else 0
ret.enableBsm = 0x58b in fingerprint[bus]
# *** panda safety config ***
if candidate in CANFD_CAR:
if candidate in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
cfgs = [get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd), ]
if CAN.ECAN >= 4:
cfgs.insert(0, get_safety_config(car.CarParams.SafetyModel.noOutput))
ret.safetyConfigs = cfgs
if ret.flags & HyundaiFlags.CANFD_HDA2:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
if ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING
if ret.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_ALT_BUTTONS
if ret.flags & HyundaiFlags.CANFD_CAMERA_SCC:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
else:
if ret.flags & HyundaiFlags.CAN_CANFD:
if candidate in LEGACY_SAFETY_MODE_CAR:
# these cars require a special panda safety mode due to missing counters and checksums in the messages
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)]
else:
cfgs = [get_safety_config(car.CarParams.SafetyModel.hyundai), ]
if CAN.ECAN >= 4:
cfgs.insert(0, get_safety_config(car.CarParams.SafetyModel.noOutput))
ret.safetyConfigs = cfgs
if ret.flags & HyundaiFlags.CANFD_HDA2:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAN_CANFD
elif candidate in LEGACY_SAFETY_MODE_CAR:
# these cars require a special panda safety mode due to missing counters and checksums in the messages
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)]
else:
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)]
if ret.flags & HyundaiFlags.CAN_CANFD_HYBRID:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAN_CANFD_HYBRID
if candidate in CAMERA_SCC_CAR:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
# these flags apply to both CANFD_CAR and CAN_CANFD_HYBRID_CAR platforms
if ret.flags & HyundaiFlags.CANFD_HDA2:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
if ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG
if ret.flags & HyundaiFlags.HYBRID:
@@ -166,6 +159,10 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.4
# FIXME: Non-HDA2 2023-24 Hyundai Palisade / Kia Telluride is disabled in this branch;
# The non-HDA2 variant is supported in another PR: https://github.com/commaai/openpilot/pull/27478
ret.dashcamOnly |= candidate in (CAR.HYUNDAI_PALISADE_2023, ) and not hda2
return ret
@staticmethod
+10 -10
View File
@@ -25,7 +25,7 @@ class CarControllerParams:
self.STEER_THRESHOLD = 150
self.STEER_STEP = 1 # 100 Hz
if CP.carFingerprint in CANFD_CAR:
if CP.carFingerprint in (CANFD_CAR - CAN_CANFD_HYBRID_CAR):
self.STEER_MAX = 270
self.STEER_DRIVER_ALLOWANCE = 250
self.STEER_DRIVER_MULTIPLIER = 2
@@ -95,8 +95,8 @@ class HyundaiFlags(IntFlag):
MIN_STEER_32_MPH = 2 ** 23
# CAN CAN-FD
CAN_CANFD = 2 ** 24
# CAN CAN-FD hybrid architecture
CAN_CANFD_HYBRID = 2 ** 24
class Footnote(Enum):
@@ -126,9 +126,6 @@ class HyundaiPlatformConfig(PlatformConfig):
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
if self.flags & HyundaiFlags.CAN_CANFD:
self.dbc_dict = dbc_dict('hyundai_palisade_2023_generated', None)
@dataclass
class HyundaiCanFDPlatformConfig(PlatformConfig):
@@ -137,6 +134,9 @@ class HyundaiCanFDPlatformConfig(PlatformConfig):
def init(self):
self.flags |= HyundaiFlags.CANFD
if self.flags & HyundaiFlags.CAN_CANFD_HYBRID:
self.dbc_dict = dbc_dict('hyundai_palisade_2023_generated', None)
class CAR(Platforms):
# Hyundai
@@ -300,13 +300,13 @@ class CAR(Platforms):
CarSpecs(mass=1999, wheelbase=2.9, steerRatio=15.6 * 1.15, tireStiffnessFactor=0.63),
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_PALISADE_2023 = HyundaiPlatformConfig(
HYUNDAI_PALISADE_2023 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Palisade (with HDA II) 2023-24", "All", car_parts=CarParts.common([CarHarness.hyundai_r])),
HyundaiCarDocs("Kia Telluride (with HDA II) 2023-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
],
HYUNDAI_PALISADE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAN_CANFD | HyundaiFlags.RADAR_SCC,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAN_CANFD_HYBRID | HyundaiFlags.RADAR_SCC,
)
HYUNDAI_VELOSTER = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e]))],
@@ -586,7 +586,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str
# Non-electric CAN FD platforms often do not have platform code specifiers needed
# to distinguish between hybrid and ICE. All EVs so far are either exclusively
# electric or specify electric in the platform code.
fuzzy_platform_blacklist = {str(c) for c in (CANFD_CAR - EV_CAR - CANFD_FUZZY_WHITELIST)}
fuzzy_platform_blacklist = {str(c) for c in (CANFD_CAR - CAN_CANFD_HYBRID_CAR - EV_CAR - CANFD_FUZZY_WHITELIST)}
candidates: set[str] = set()
for candidate, fws in offline_fw_versions.items():
@@ -743,7 +743,7 @@ CAN_GEARS = {
CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC)
CAN_CANFD_CAR = CAR.with_flags(HyundaiFlags.CAN_CANFD)
CAN_CANFD_HYBRID_CAR = CAR.with_flags(HyundaiFlags.CAN_CANFD_HYBRID)
# These CAN FD cars do not accept communication control to disable the ADAS ECU,
# responds with 0x7F2822 - 'conditions not correct'
+4 -2
View File
@@ -4,12 +4,13 @@ import time
from cereal import car, log, messaging
from openpilot.common.params import Params
from openpilot.selfdrive.manager.process_config import managed_processes
from openpilot.system.hardware import HARDWARE
if __name__ == "__main__":
CP = car.CarParams(notCar=True)
CP = car.CarParams(notCar=True, wheelbase=1, steerRatio=10)
Params().put("CarParams", CP.to_bytes())
procs = ['camerad', 'ui', 'modeld', 'calibrationd']
procs = ['camerad', 'ui', 'modeld', 'calibrationd', 'plannerd', 'dmonitoringmodeld', 'dmonitoringd']
for p in procs:
managed_processes[p].start()
@@ -17,6 +18,7 @@ if __name__ == "__main__":
msgs = {s: messaging.new_message(s) for s in ['controlsState', 'deviceState', 'carParams']}
msgs['deviceState'].deviceState.started = True
msgs['deviceState'].deviceState.deviceType = HARDWARE.get_device_type()
msgs['carParams'].carParams.openpilotLongitudinalControl = True
msgs['pandaStates'] = messaging.new_message('pandaStates', 1)
+8 -5
View File
@@ -123,12 +123,15 @@ void MapWindow::updateState(const UIState &s) {
auto locationd_pos = locationd_location.getPositionGeodetic();
auto locationd_orientation = locationd_location.getCalibratedOrientationNED();
auto locationd_velocity = locationd_location.getVelocityCalibrated();
auto locationd_ecef = locationd_location.getPositionECEF();
// Check std norm
auto pos_ecef_std = locationd_location.getPositionECEF().getStd();
bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100;
locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough);
locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && locationd_ecef.getValid());
if (locationd_valid) {
// Check std norm
auto pos_ecef_std = locationd_ecef.getStd();
bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100;
locationd_valid = pos_accurate_enough;
}
if (locationd_valid) {
last_position = QMapLibre::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]);
+21 -3
View File
@@ -1,5 +1,7 @@
#include "selfdrive/ui/qt/network/wifi_manager.h"
#include <utility>
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/widgets/prime.h"
@@ -14,9 +16,15 @@ bool compare_by_strength(const Network &a, const Network &b) {
template <typename T = QDBusMessage, typename... Args>
T call(const QString &path, const QString &interface, const QString &method, Args &&...args) {
QDBusInterface nm = QDBusInterface(NM_DBUS_SERVICE, path, interface, QDBusConnection::systemBus());
QDBusInterface nm(NM_DBUS_SERVICE, path, interface, QDBusConnection::systemBus());
nm.setTimeout(DBUS_TIMEOUT);
QDBusMessage response = nm.call(method, args...);
QDBusMessage response = nm.call(method, std::forward<Args>(args)...);
if (response.type() == QDBusMessage::ErrorMessage) {
qCritical() << "DBus call error:" << response.errorMessage();
return T();
}
if constexpr (std::is_same_v<T, QDBusMessage>) {
return response;
} else if (response.arguments().count() >= 1) {
@@ -99,10 +107,20 @@ void WifiManager::refreshFinished(QDBusPendingCallWatcher *watcher) {
seenNetworks.clear();
const QDBusReply<QList<QDBusObjectPath>> watcher_reply = *watcher;
if (!watcher_reply.isValid()) {
qCritical() << "Failed to refresh";
watcher->deleteLater();
return;
}
for (const QDBusObjectPath &path : watcher_reply.value()) {
QDBusReply<QVariantMap> reply = call(path.path(), NM_DBUS_INTERFACE_PROPERTIES, "GetAll", NM_DBUS_INTERFACE_ACCESS_POINT);
auto properties = reply.value();
if (!reply.isValid()) {
qCritical() << "Failed to retrieve properties for path:" << path.path();
continue;
}
auto properties = reply.value();
const QByteArray ssid = properties["Ssid"].toByteArray();
if (ssid.isEmpty()) continue;
+5 -11
View File
@@ -31,8 +31,7 @@ int CameraState::clear_req_queue() {
req_mgr_flush_request.session_hdl = session_handle;
req_mgr_flush_request.link_hdl = link_handle;
req_mgr_flush_request.flush_type = CAM_REQ_MGR_FLUSH_TYPE_ALL;
int ret;
ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request));
int ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request));
// LOGD("flushed all req: %d", ret);
return ret;
}
@@ -470,7 +469,6 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num
enabled = enabled_;
if (!enabled) return;
int ret;
sensor_fd = open_v4l_by_name_and_index("cam-sensor-driver", camera_num);
assert(sensor_fd >= 0);
LOGD("opened sensor for %d", camera_num);
@@ -501,7 +499,7 @@ void CameraState::camera_open(MultiCameraState *multi_cam_state_, int camera_num
// create session
struct cam_req_mgr_session_info session_info = {};
ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_CREATE_SESSION, &session_info, sizeof(session_info));
int ret = do_cam_control(multi_cam_state->video0_fd, CAM_REQ_MGR_CREATE_SESSION, &session_info, sizeof(session_info));
LOGD("get session: %d 0x%X", ret, session_info.session_hdl);
session_handle = session_info.session_hdl;
@@ -654,8 +652,6 @@ void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_i
}
void cameras_open(MultiCameraState *s) {
int ret;
LOG("-- Opening devices");
// video0 is req_mgr, the target of many ioctls
s->video0_fd = HANDLE_EINTR(open("/dev/v4l/by-path/platform-soc:qcom_cam-req-mgr-video-index0", O_RDWR | O_NONBLOCK));
@@ -679,7 +675,7 @@ void cameras_open(MultiCameraState *s) {
query_cap_cmd.handle_type = 1;
query_cap_cmd.caps_handle = (uint64_t)&isp_query_cap_cmd;
query_cap_cmd.size = sizeof(isp_query_cap_cmd);
ret = do_cam_control(s->isp_fd, CAM_QUERY_CAP, &query_cap_cmd, sizeof(query_cap_cmd));
int ret = do_cam_control(s->isp_fd, CAM_QUERY_CAP, &query_cap_cmd, sizeof(query_cap_cmd));
assert(ret == 0);
LOGD("using MMU handle: %x", isp_query_cap_cmd.device_iommu.non_secure);
LOGD("using MMU handle: %x", isp_query_cap_cmd.cdm_iommu.non_secure);
@@ -703,15 +699,13 @@ void cameras_open(MultiCameraState *s) {
}
void CameraState::camera_close() {
int ret;
// stop devices
LOG("-- Stop devices %d", camera_num);
if (enabled) {
// ret = device_control(sensor_fd, CAM_STOP_DEV, session_handle, sensor_dev_handle);
// LOGD("stop sensor: %d", ret);
ret = device_control(multi_cam_state->isp_fd, CAM_STOP_DEV, session_handle, isp_dev_handle);
int ret = device_control(multi_cam_state->isp_fd, CAM_STOP_DEV, session_handle, isp_dev_handle);
LOGD("stop isp: %d", ret);
ret = device_control(csiphy_fd, CAM_STOP_DEV, session_handle, csiphy_dev_handle);
LOGD("stop csiphy: %d", ret);
@@ -746,7 +740,7 @@ void CameraState::camera_close() {
LOGD("released buffers");
}
ret = device_control(sensor_fd, CAM_RELEASE_DEV, session_handle, sensor_dev_handle);
int ret = device_control(sensor_fd, CAM_RELEASE_DEV, session_handle, sensor_dev_handle);
LOGD("release sensor: %d", ret);
// destroyed session
+1 -2
View File
@@ -86,11 +86,10 @@ void *alloc_w_mmu_hdl(int video0_fd, int len, uint32_t *handle, int align, int f
}
void release(int video0_fd, uint32_t handle) {
int ret;
struct cam_mem_mgr_release_cmd mem_mgr_release_cmd = {0};
mem_mgr_release_cmd.buf_handle = handle;
ret = do_cam_control(video0_fd, CAM_REQ_MGR_RELEASE_BUF, &mem_mgr_release_cmd, sizeof(mem_mgr_release_cmd));
int ret = do_cam_control(video0_fd, CAM_REQ_MGR_RELEASE_BUF, &mem_mgr_release_cmd, sizeof(mem_mgr_release_cmd));
assert(ret == 0);
}
+1 -2
View File
@@ -12,8 +12,7 @@ int main(int argc, char *argv[]) {
return 0;
}
int ret;
ret = util::set_realtime_priority(53);
int ret = util::set_realtime_priority(53);
assert(ret == 0);
ret = util::set_core_affinity({6});
assert(ret == 0 || Params().getBool("IsOffroad")); // failure ok while offroad due to offlining cores
+4 -2
View File
@@ -111,6 +111,8 @@ void ChartView::setTheme(QChart::ChartTheme theme) {
axis_y->setLabelsBrush(palette().text());
chart()->legend()->setLabelColor(palette().color(QPalette::Text));
}
axis_x->setLineVisible(false);
axis_y->setLineVisible(false);
for (auto &s : sigs) {
s.series->setColor(s.sig->color);
}
@@ -745,8 +747,8 @@ void ChartView::drawTimeline(QPainter *painter) {
const auto plot_area = chart()->plotArea();
// draw vertical time line
qreal x = std::clamp(chart()->mapToPosition(QPointF{cur_sec, 0}).x(), plot_area.left(), plot_area.right());
painter->setPen(QPen(chart()->titleBrush().color(), 2));
painter->drawLine(QPointF{x, plot_area.top()}, QPointF{x, plot_area.bottom() + 1});
painter->setPen(QPen(chart()->titleBrush().color(), 1));
painter->drawLine(QPointF{x, plot_area.top() - 1}, QPointF{x, plot_area.bottom() + 1});
// draw current time under the axis-x
QString time_str = QString::number(cur_sec, 'f', 2);
+2 -2
View File
@@ -29,11 +29,11 @@ struct MessageId {
}
bool operator<(const MessageId &other) const {
return std::pair{source, address} < std::pair{other.source, other.address};
return std::tie(source, address) < std::tie(other.source, other.address);
}
bool operator>(const MessageId &other) const {
return std::pair{source, address} > std::pair{other.source, other.address};
return std::tie(source, address) > std::tie(other.source, other.address);
}
};
+145 -98
View File
@@ -3,7 +3,6 @@
#include <QFile>
#include <QFileInfo>
#include <QRegularExpression>
#include <QTextStream>
DBCFile::DBCFile(const QString &dbc_file_name) {
QFile file(dbc_file_name);
@@ -76,117 +75,44 @@ cabana::Msg *DBCFile::msg(const QString &name) {
return it != msgs.end() ? &(it->second) : nullptr;
}
cabana::Signal *DBCFile::signal(uint32_t address, const QString name) {
auto m = msg(address);
return m ? (cabana::Signal *)m->sig(name) : nullptr;
}
void DBCFile::parse(const QString &content) {
static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))");
static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
static QRegularExpression msg_comment_regexp(R"(^CM_ BO_ *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)");
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)");
static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))");
msgs.clear();
int line_num = 0;
QString line;
auto dbc_assert = [&line_num, &line, this](bool condition, const QString &msg = "") {
if (!condition) throw std::runtime_error(QString("[%1:%2]%3: %4").arg(filename).arg(line_num).arg(msg).arg(line).toStdString());
};
auto get_sig = [this](uint32_t address, const QString &name) -> cabana::Signal * {
auto m = (cabana::Msg *)msg(address);
return m ? (cabana::Signal *)m->sig(name) : nullptr;
};
msgs.clear();
QTextStream stream((QString *)&content);
cabana::Msg *current_msg = nullptr;
int multiplexor_cnt = 0;
bool seen_first = false;
QTextStream stream((QString *)&content);
while (!stream.atEnd()) {
++line_num;
QString raw_line = stream.readLine();
line = raw_line.trimmed();
bool seen = true;
if (line.startsWith("BO_ ")) {
multiplexor_cnt = 0;
auto match = bo_regexp.match(line);
dbc_assert(match.hasMatch());
auto address = match.captured(1).toUInt();
dbc_assert(msgs.count(address) == 0, QString("Duplicate message address: %1").arg(address));
current_msg = &msgs[address];
current_msg->address = address;
current_msg->name = match.captured(2);
current_msg->size = match.captured(3).toULong();
current_msg->transmitter = match.captured(4).trimmed();
} else if (line.startsWith("SG_ ")) {
int offset = 0;
auto match = sg_regexp.match(line);
if (!match.hasMatch()) {
match = sgm_regexp.match(line);
offset = 1;
try {
if (line.startsWith("BO_ ")) {
multiplexor_cnt = 0;
current_msg = parseBO(line);
} else if (line.startsWith("SG_ ")) {
parseSG(line, current_msg, multiplexor_cnt);
} else if (line.startsWith("VAL_ ")) {
parseVAL(line);
} else if (line.startsWith("CM_ BO_")) {
parseCM_BO(line, content, raw_line, stream);
} else if (line.startsWith("CM_ SG_ ")) {
parseCM_SG(line, content, raw_line, stream);
} else {
seen = false;
}
dbc_assert(match.hasMatch());
dbc_assert(current_msg, "No Message");
auto name = match.captured(1);
dbc_assert(current_msg->sig(name) == nullptr, "Duplicate signal name");
cabana::Signal s{};
if (offset == 1) {
auto indicator = match.captured(2);
if (indicator == "M") {
// Only one signal within a single message can be the multiplexer switch.
dbc_assert(++multiplexor_cnt < 2, "Multiple multiplexor");
s.type = cabana::Signal::Type::Multiplexor;
} else {
s.type = cabana::Signal::Type::Multiplexed;
s.multiplex_value = indicator.mid(1).toInt();
}
}
s.name = name;
s.start_bit = match.captured(offset + 2).toInt();
s.size = match.captured(offset + 3).toInt();
s.is_little_endian = match.captured(offset + 4).toInt() == 1;
s.is_signed = match.captured(offset + 5) == "-";
s.factor = match.captured(offset + 6).toDouble();
s.offset = match.captured(offset + 7).toDouble();
s.min = match.captured(8 + offset).toDouble();
s.max = match.captured(9 + offset).toDouble();
s.unit = match.captured(10 + offset);
s.receiver_name = match.captured(11 + offset).trimmed();
current_msg->sigs.push_back(new cabana::Signal(s));
} else if (line.startsWith("VAL_ ")) {
auto match = val_regexp.match(line);
dbc_assert(match.hasMatch());
if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) {
QStringList desc_list = match.captured(3).trimmed().split('"');
for (int i = 0; i < desc_list.size(); i += 2) {
auto val = desc_list[i].trimmed();
if (!val.isEmpty() && (i + 1) < desc_list.size()) {
auto desc = desc_list[i + 1].trimmed();
s->val_desc.push_back({val.toDouble(), desc});
}
}
}
} else if (line.startsWith("CM_ BO_")) {
if (!line.endsWith("\";")) {
int pos = stream.pos() - raw_line.length() - 1;
line = content.mid(pos, content.indexOf("\";", pos));
}
auto match = msg_comment_regexp.match(line);
dbc_assert(match.hasMatch());
if (auto m = (cabana::Msg *)msg(match.captured(1).toUInt())) {
m->comment = match.captured(2).trimmed().replace("\\\"", "\"");
}
} else if (line.startsWith("CM_ SG_ ")) {
if (!line.endsWith("\";")) {
int pos = stream.pos() - raw_line.length() - 1;
line = content.mid(pos, content.indexOf("\";", pos));
}
auto match = sg_comment_regexp.match(line);
dbc_assert(match.hasMatch());
if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) {
s->comment = match.captured(3).trimmed().replace("\\\"", "\"");
}
} else {
seen = false;
} catch (std::exception &e) {
throw std::runtime_error(QString("[%1:%2]%3: %4").arg(filename).arg(line_num).arg(e.what()).arg(line).toStdString());
}
if (seen) {
@@ -201,6 +127,127 @@ void DBCFile::parse(const QString &content) {
}
}
cabana::Msg *DBCFile::parseBO(const QString &line) {
static QRegularExpression bo_regexp(R"(^BO_ (?<address>\w+) (?<name>\w+) *: (?<size>\w+) (?<transmitter>\w+))");
QRegularExpressionMatch match = bo_regexp.match(line);
if (!match.hasMatch())
throw std::runtime_error("Invalid BO_ line format");
uint32_t address = match.captured("address").toUInt();
if (msgs.count(address) > 0)
throw std::runtime_error(QString("Duplicate message address: %1").arg(address).toStdString());
// Create a new message object
cabana::Msg *msg = &msgs[address];
msg->address = address;
msg->name = match.captured("name");
msg->size = match.captured("size").toULong();
msg->transmitter = match.captured("transmitter").trimmed();
return msg;
}
void DBCFile::parseCM_BO(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream) {
static QRegularExpression msg_comment_regexp(R"(^CM_ BO_ *(?<address>\w+) *\"(?<comment>(?:[^"\\]|\\.)*)\"\s*;)");
QString parse_line = line;
if (!parse_line.endsWith("\";")) {
int pos = stream.pos() - raw_line.length() - 1;
parse_line = content.mid(pos, content.indexOf("\";", pos));
}
auto match = msg_comment_regexp.match(parse_line);
if (!match.hasMatch())
throw std::runtime_error("Invalid message comment format");
if (auto m = (cabana::Msg *)msg(match.captured("address").toUInt()))
m->comment = match.captured("comment").trimmed().replace("\\\"", "\"");
}
void DBCFile::parseSG(const QString &line, cabana::Msg *current_msg, int &multiplexor_cnt) {
static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
if (!current_msg)
throw std::runtime_error("No Message");
int offset = 0;
auto match = sg_regexp.match(line);
if (!match.hasMatch()) {
match = sgm_regexp.match(line);
offset = 1;
}
if (!match.hasMatch())
throw std::runtime_error("Invalid SG_ line format");
QString name = match.captured(1);
if (current_msg->sig(name) != nullptr)
throw std::runtime_error("Duplicate signal name");
cabana::Signal s{};
if (offset == 1) {
auto indicator = match.captured(2);
if (indicator == "M") {
++multiplexor_cnt;
// Only one signal within a single message can be the multiplexer switch.
if (multiplexor_cnt >= 2)
throw std::runtime_error("Multiple multiplexor");
s.type = cabana::Signal::Type::Multiplexor;
} else {
s.type = cabana::Signal::Type::Multiplexed;
s.multiplex_value = indicator.mid(1).toInt();
}
}
s.name = name;
s.start_bit = match.captured(offset + 2).toInt();
s.size = match.captured(offset + 3).toInt();
s.is_little_endian = match.captured(offset + 4).toInt() == 1;
s.is_signed = match.captured(offset + 5) == "-";
s.factor = match.captured(offset + 6).toDouble();
s.offset = match.captured(offset + 7).toDouble();
s.min = match.captured(8 + offset).toDouble();
s.max = match.captured(9 + offset).toDouble();
s.unit = match.captured(10 + offset);
s.receiver_name = match.captured(11 + offset).trimmed();
current_msg->sigs.push_back(new cabana::Signal(s));
}
void DBCFile::parseCM_SG(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream) {
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)");
QString parse_line = line;
if (!parse_line.endsWith("\";")) {
int pos = stream.pos() - raw_line.length() - 1;
parse_line = content.mid(pos, content.indexOf("\";", pos));
}
auto match = sg_comment_regexp.match(parse_line);
if (!match.hasMatch())
throw std::runtime_error("Invalid CM_ SG_ line format");
if (auto s = signal(match.captured(1).toUInt(), match.captured(2))) {
s->comment = match.captured(3).trimmed().replace("\\\"", "\"");
}
}
void DBCFile::parseVAL(const QString &line) {
static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))");
auto match = val_regexp.match(line);
if (!match.hasMatch())
throw std::runtime_error("invalid VAL_ line format");
if (auto s = signal(match.captured(1).toUInt(), match.captured(2))) {
QStringList desc_list = match.captured(3).trimmed().split('"');
for (int i = 0; i < desc_list.size(); i += 2) {
auto val = desc_list[i].trimmed();
if (!val.isEmpty() && (i + 1) < desc_list.size()) {
auto desc = desc_list[i + 1].trimmed();
s->val_desc.push_back({val.toDouble(), desc});
}
}
}
}
QString DBCFile::generateDBC() {
QString dbc_string, comment, val_desc;
for (const auto &[address, m] : msgs) {
+8
View File
@@ -1,6 +1,7 @@
#pragma once
#include <map>
#include <QTextStream>
#include "tools/cabana/dbc/dbc.h"
@@ -26,6 +27,7 @@ public:
cabana::Msg *msg(uint32_t address);
cabana::Msg *msg(const QString &name);
inline cabana::Msg *msg(const MessageId &id) { return msg(id.address); }
cabana::Signal *signal(uint32_t address, const QString name);
inline QString name() const { return name_.isEmpty() ? "untitled" : name_; }
inline bool isEmpty() const { return msgs.empty() && name_.isEmpty(); }
@@ -34,6 +36,12 @@ public:
private:
void parse(const QString &content);
cabana::Msg *parseBO(const QString &line);
void parseSG(const QString &line, cabana::Msg *current_msg, int &multiplexor_cnt);
void parseCM_BO(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream);
void parseCM_SG(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream);
void parseVAL(const QString &line);
QString header;
std::map<uint32_t, cabana::Msg> msgs;
QString name_;
+1 -1
View File
@@ -53,7 +53,7 @@ public:
std::function<bool(double, double)> filter_cmp = nullptr;
std::deque<Message> messages;
std::vector<cabana::Signal *> sigs;
bool hex_mode = true;
bool hex_mode = false;
};
class LogsWidget : public QFrame {
+18 -26
View File
@@ -43,7 +43,6 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget
view->setItemsExpandable(false);
view->setIndentation(0);
view->setRootIsDecorated(false);
view->setUniformRowHeights(!settings.multiple_lines_hex);
// Must be called before setting any header parameters to avoid overriding
restoreHeaderState(settings.message_header_state);
@@ -135,15 +134,9 @@ void MessagesWidget::selectMessage(const MessageId &msg_id) {
}
void MessagesWidget::suppressHighlighted() {
if (sender() == suppress_add) {
size_t n = can->suppressHighlighted();
suppress_clear->setText(tr("Clear (%1)").arg(n));
suppress_clear->setEnabled(true);
} else {
can->clearSuppressed();
suppress_clear->setText(tr("Clear"));
suppress_clear->setEnabled(false);
}
int n = sender() == suppress_add ? can->suppressHighlighted() : (can->clearSuppressed(), 0);
suppress_clear->setText(n > 0 ? tr("Clear (%1)").arg(n) : tr("Clear"));
suppress_clear->setEnabled(n > 0);
}
void MessagesWidget::headerContextMenuEvent(const QPoint &pos) {
@@ -174,7 +167,6 @@ void MessagesWidget::menuAboutToShow() {
void MessagesWidget::setMultiLineBytes(bool multi) {
settings.multiple_lines_hex = multi;
delegate->setMultipleLines(multi);
view->setUniformRowHeights(!multi);
view->updateBytesSectionSize();
view->doItemsLayout();
}
@@ -207,22 +199,22 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const {
}
};
const static QString NA = QStringLiteral("N/A");
const auto &item = items_[index.row()];
const auto &data = can->lastMessage(item.id);
if (role == Qt::DisplayRole) {
switch (index.column()) {
case Column::NAME: return item.name;
case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : "N/A";
case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : NA;
case Column::ADDRESS: return QString::number(item.id.address, 16);
case Column::NODE: return item.node;
case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(data.freq) : "N/A";
case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(data.count) : "N/A";
case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : "N/A";
case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(can->lastMessage(item.id).freq) : NA;
case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(can->lastMessage(item.id).count) : NA;
case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : NA;
}
} else if (role == ColorsRole) {
return QVariant::fromValue((void*)(&data.colors));
return QVariant::fromValue((void*)(&can->lastMessage(item.id).colors));
} else if (role == BytesRole && index.column() == Column::DATA && item.id.source != INVALID_SOURCE) {
return QVariant::fromValue((void*)(&data.dat));
return QVariant::fromValue((void*)(&can->lastMessage(item.id).dat));
} else if (role == Qt::ForegroundRole && !item.active) {
return settings.theme == DARK_THEME ? QApplication::palette().color(QPalette::Text).darker(150) : QColor(Qt::gray);
} else if (role == Qt::ToolTipRole && index.column() == Column::NAME) {
@@ -366,18 +358,17 @@ bool MessageListModel::filterAndSort() {
}
void MessageListModel::msgsReceived(const std::set<MessageId> *new_msgs, bool has_new_ids) {
if (has_new_ids || filters_.contains(Column::FREQ) || filters_.contains(Column::COUNT) || filters_.contains(Column::DATA)) {
if (has_new_ids || ((filters_.count(Column::FREQ) || filters_.count(Column::COUNT) || filters_.count(Column::DATA)) &&
++sort_threshold_ == settings.fps)) {
sort_threshold_ = 0;
if (filterAndSort()) return;
}
for (int i = 0; i < items_.size(); ++i) {
auto &item = items_[i];
bool prev_active = item.active;
for (auto &item : items_) {
item.active = isMessageActive(item.id);
if (item.active != prev_active || !new_msgs || new_msgs->count(item.id)) {
for (int col = 0; col < columnCount(); ++col)
emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole});
}
}
// Update viewport
emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
}
void MessageListModel::sort(int column, Qt::SortOrder order) {
@@ -420,6 +411,7 @@ void MessageView::updateBytesSectionSize() {
max_bytes = std::max<int>(max_bytes, m.dat.size());
}
}
setUniformRowHeights(!delegate->multipleLines() || max_bytes <= 8);
header()->resizeSection(MessageListModel::Column::DATA, delegate->sizeForBytes(max_bytes).width());
}
+1
View File
@@ -61,6 +61,7 @@ private:
std::set<MessageId> dbc_messages_;
int sort_column = 0;
Qt::SortOrder sort_order = Qt::AscendingOrder;
int sort_threshold_ = 0;
};
class MessageView : public QTreeView {
+4 -1
View File
@@ -12,6 +12,9 @@
#include "tools/cabana/utils/util.h"
const int MIN_CACHE_MINIUTES = 30;
const int MAX_CACHE_MINIUTES = 120;
Settings settings;
template <class SettingOperation>
@@ -72,7 +75,7 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
fps->setValue(settings.fps);
form_layout->addRow(tr("Max Cached Minutes"), cached_minutes = new QSpinBox(this));
cached_minutes->setRange(5, 60);
cached_minutes->setRange(MIN_CACHE_MINIUTES, MAX_CACHE_MINIUTES);
cached_minutes->setSingleStep(1);
cached_minutes->setValue(settings.max_cached_minutes);
main_layout->addWidget(groupbox);
+9 -9
View File
@@ -130,6 +130,7 @@ void AbstractStream::updateLastMsgsTo(double sec) {
current_sec_ = sec;
uint64_t last_ts = (sec + routeStartTime()) * 1e9;
std::unordered_map<MessageId, CanData> msgs;
msgs.reserve(events_.size());
for (const auto &[id, ev] : events_) {
auto it = std::upper_bound(ev.begin(), ev.end(), last_ts, CompareCanEvent());
@@ -171,6 +172,8 @@ const CanEvent *AbstractStream::newEvent(uint64_t mono_time, const cereal::CanDa
void AbstractStream::mergeEvents(const std::vector<const CanEvent *> &events) {
static MessageEventsMap msg_events;
std::for_each(msg_events.begin(), msg_events.end(), [](auto &e) { e.second.clear(); });
// Group events by message ID
for (auto e : events) {
msg_events[{.source = e->src, .address = e->address}].push_back(e);
}
@@ -207,7 +210,7 @@ inline QColor blend(const QColor &a, const QColor &b) {
return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2);
}
// Calculate the frequency of the past minute.
// Calculate the frequency from the past one minute data
double calc_freq(const MessageId &msg_id, double current_sec) {
const auto &events = can->events(msg_id);
uint64_t cur_mono_time = (can->routeStartTime() + current_sec) * 1e9;
@@ -255,11 +258,8 @@ void CanData::compute(const MessageId &msg_id, const uint8_t *can_data, const in
if (last != cur) {
const int delta = cur - last;
// Keep track if signal is changing randomly, or mostly moving in the same direction
if (std::signbit(delta) == std::signbit(last_change.delta)) {
last_change.same_delta_counter = std::min(16, last_change.same_delta_counter + 1);
} else {
last_change.same_delta_counter = std::max(0, last_change.same_delta_counter - 4);
}
last_change.same_delta_counter += std::signbit(delta) == std::signbit(last_change.delta) ? 1 : -4;
last_change.same_delta_counter = std::clamp(last_change.same_delta_counter, 0, 16);
const double delta_t = ts - last_change.ts;
// Mostly moves in the same direction, color based on delta up/down
@@ -272,10 +272,10 @@ void CanData::compute(const MessageId &msg_id, const uint8_t *can_data, const in
}
// Track bit level changes
const uint8_t tmp = (cur ^ last);
const uint8_t diff = (cur ^ last);
for (int bit = 0; bit < 8; bit++) {
if (tmp & (1 << (7 - bit))) {
last_change.bit_change_counts[bit] += 1;
if (diff & (1u << bit)) {
++last_change.bit_change_counts[7 - bit];
}
}
+22 -16
View File
@@ -51,7 +51,8 @@ std::pair<double, double> SegmentTree::get_minmax(int n, int left, int right, in
// MessageBytesDelegate
MessageBytesDelegate::MessageBytesDelegate(QObject *parent, bool multiple_lines) : multiple_lines(multiple_lines), QStyledItemDelegate(parent) {
MessageBytesDelegate::MessageBytesDelegate(QObject *parent, bool multiple_lines)
: font_metrics(QApplication::font()), multiple_lines(multiple_lines), QStyledItemDelegate(parent) {
fixed_font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
byte_size = QFontMetrics(fixed_font).size(Qt::TextSingleLine, "00 ") + QSize(0, 2);
for (int i = 0; i < 256; ++i) {
@@ -73,31 +74,38 @@ QSize MessageBytesDelegate::sizeHint(const QStyleOptionViewItem &option, const Q
}
void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
auto data = index.data(BytesRole);
if (!data.isValid()) {
return QStyledItemDelegate::paint(painter, option, index);
}
QFont old_font = painter->font();
QPen old_pen = painter->pen();
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.brush(QPalette::Normal, QPalette::Highlight));
}
const QPoint pt{option.rect.left() + h_margin, option.rect.top() + v_margin};
painter->setFont(fixed_font);
const auto &bytes = *static_cast<std::vector<uint8_t>*>(data.value<void*>());
const auto &colors = *static_cast<std::vector<QColor>*>(index.data(ColorsRole).value<void*>());
QRect item_rect = option.rect.adjusted(h_margin, v_margin, -h_margin, -v_margin);
QColor highlighted_color = option.palette.color(QPalette::HighlightedText);
auto text_color = index.data(Qt::ForegroundRole).value<QColor>();
bool inactive = text_color.isValid();
if (!inactive) {
text_color = option.palette.color(QPalette::Text);
}
auto data = index.data(BytesRole);
if (!data.isValid()) {
painter->setFont(option.font);
painter->setPen(option.state & QStyle::State_Selected ? highlighted_color : text_color);
QString text = font_metrics.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, item_rect.width());
painter->drawText(item_rect, Qt::AlignLeft | Qt::AlignVCenter, text);
return;
}
// Paint hex column
const auto &bytes = *static_cast<std::vector<uint8_t> *>(data.value<void *>());
const auto &colors = *static_cast<std::vector<QColor> *>(index.data(ColorsRole).value<void *>());
painter->setFont(fixed_font);
const QPen text_pen(option.state & QStyle::State_Selected ? highlighted_color : text_color);
const QPoint pt = item_rect.topLeft();
for (int i = 0; i < bytes.size(); ++i) {
int row = !multiple_lines ? 0 : i / 8;
int column = !multiple_lines ? i : i % 8;
QRect r = QRect({pt.x() + column * byte_size.width(), pt.y() + row * byte_size.height()}, byte_size);
QRect r({pt.x() + column * byte_size.width(), pt.y() + row * byte_size.height()}, byte_size);
if (!inactive && i < colors.size() && colors[i].alpha() > 0) {
if (option.state & QStyle::State_Selected) {
painter->setPen(option.palette.color(QPalette::Text));
@@ -105,12 +113,10 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
}
painter->fillRect(r, colors[i]);
} else {
painter->setPen(option.state & QStyle::State_Selected ? option.palette.color(QPalette::HighlightedText) : text_color);
painter->setPen(text_pen);
}
utils::drawStaticText(painter, r, hex_text_table[bytes[i]]);
}
painter->setFont(old_font);
painter->setPen(old_pen);
}
// TabBar
+2
View File
@@ -9,6 +9,7 @@
#include <QByteArray>
#include <QDoubleValidator>
#include <QFont>
#include <QFontMetrics>
#include <QPainter>
#include <QRegExpValidator>
#include <QSocketNotifier>
@@ -76,6 +77,7 @@ public:
private:
std::array<QStaticText, 256> hex_text_table;
QFontMetrics font_metrics;
QFont fixed_font;
QSize byte_size = {};
bool multiple_lines = false;
+2 -2
View File
@@ -54,8 +54,8 @@ fi
eval "$(pyenv init --path)"
echo "update pip"
pip install pip==23.3
pip install poetry==1.6.1
pip install pip==24.0
pip install poetry==1.7.0
poetry config virtualenvs.prefer-active-python true --local
poetry config virtualenvs.in-project true --local
+99 -63
View File
@@ -1,5 +1,10 @@
#include "tools/replay/framereader.h"
#include <map>
#include <memory>
#include <tuple>
#include <utility>
#include "common/util.h"
#include "third_party/libyuv/include/libyuv.h"
#include "tools/replay/util.h"
@@ -12,7 +17,6 @@
#define HW_PIX_FMT AV_PIX_FMT_CUDA
#endif
namespace {
enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) {
@@ -21,11 +25,32 @@ enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *
if (*p == *hw_pix_fmt) return *p;
}
rWarning("Please run replay with the --no-hw-decoder flag!");
// fallback to YUV420p
*hw_pix_fmt = AV_PIX_FMT_NONE;
return AV_PIX_FMT_YUV420P;
}
struct DecoderManager {
VideoDecoder *acquire(CameraType type, AVCodecParameters *codecpar, bool hw_decoder) {
auto key = std::tuple(type, codecpar->width, codecpar->height);
std::unique_lock lock(mutex_);
if (auto it = decoders_.find(key); it != decoders_.end()) {
return it->second.get();
}
auto decoder = std::make_unique<VideoDecoder>();
if (!decoder->open(codecpar, hw_decoder)) {
decoder.reset(nullptr);
}
decoders_[key] = std::move(decoder);
return decoders_[key].get();
}
std::mutex mutex_;
std::map<std::tuple<CameraType, int, int>, std::unique_ptr<VideoDecoder>> decoders_;
};
DecoderManager decoder_manager;
} // namespace
FrameReader::FrameReader() {
@@ -33,12 +58,10 @@ FrameReader::FrameReader() {
}
FrameReader::~FrameReader() {
if (decoder_ctx) avcodec_free_context(&decoder_ctx);
if (input_ctx) avformat_close_input(&input_ctx);
if (hw_device_ctx) av_buffer_unref(&hw_device_ctx);
}
bool FrameReader::load(const std::string &url, bool no_hw_decoder, std::atomic<bool> *abort, bool local_cache, int chunk_size, int retries) {
bool FrameReader::load(CameraType type, const std::string &url, bool no_hw_decoder, std::atomic<bool> *abort, bool local_cache, int chunk_size, int retries) {
auto local_file_path = url.find("https://") == 0 ? cacheFilePath(url) : url;
if (!util::file_exists(local_file_path)) {
FileReader f(local_cache, chunk_size, retries);
@@ -46,10 +69,10 @@ bool FrameReader::load(const std::string &url, bool no_hw_decoder, std::atomic<b
return false;
}
}
return loadFromFile(local_file_path, no_hw_decoder, abort);
return loadFromFile(type, local_file_path, no_hw_decoder, abort);
}
bool FrameReader::loadFromFile(const std::string &file, bool no_hw_decoder, std::atomic<bool> *abort) {
bool FrameReader::loadFromFile(CameraType type, const std::string &file, bool no_hw_decoder, std::atomic<bool> *abort) {
if (avformat_open_input(&input_ctx, file.c_str(), nullptr, nullptr) != 0 ||
avformat_find_stream_info(input_ctx, nullptr) < 0) {
rError("Failed to open input file or find video stream");
@@ -57,27 +80,12 @@ bool FrameReader::loadFromFile(const std::string &file, bool no_hw_decoder, std:
}
input_ctx->probesize = 10 * 1024 * 1024; // 10MB
AVStream *video = input_ctx->streams[0];
const AVCodec *decoder = avcodec_find_decoder(video->codecpar->codec_id);
if (!decoder) return false;
decoder_ctx = avcodec_alloc_context3(decoder);
if (!decoder_ctx || avcodec_parameters_to_context(decoder_ctx, video->codecpar) != 0) {
rError("Failed to allocate or initialize codec context");
return false;
}
width = (decoder_ctx->width + 3) & ~3;
height = decoder_ctx->height;
if (has_hw_decoder && !no_hw_decoder && !initHardwareDecoder(HW_DEVICE_TYPE)) {
rWarning("No device with hardware decoder found. fallback to CPU decoding.");
}
if (avcodec_open2(decoder_ctx, decoder, nullptr) < 0) {
rError("Failed to open codec");
decoder_ = decoder_manager.acquire(type, input_ctx->streams[0]->codecpar, !no_hw_decoder);
if (!decoder_) {
return false;
}
width = decoder_->width;
height = decoder_->height;
AVPacket pkt;
packets_info.reserve(60 * 20); // 20fps, one minute
@@ -85,29 +93,70 @@ bool FrameReader::loadFromFile(const std::string &file, bool no_hw_decoder, std:
packets_info.emplace_back(PacketInfo{.flags = pkt.flags, .pos = pkt.pos});
av_packet_unref(&pkt);
}
avio_seek(input_ctx->pb, 0, SEEK_SET);
return !packets_info.empty();
}
bool FrameReader::initHardwareDecoder(AVHWDeviceType hw_device_type) {
for (int i = 0;; i++) {
const AVCodecHWConfig *config = avcodec_get_hw_config(decoder_ctx->codec, i);
if (!config) {
rWarning("decoder %s does not support hw device type %s.", decoder_ctx->codec->name,
av_hwdevice_get_type_name(hw_device_type));
return false;
}
bool FrameReader::get(int idx, VisionBuf *buf) {
if (!buf || idx < 0 || idx >= packets_info.size()) {
return false;
}
return decoder_->decode(this, idx, buf);
}
// class VideoDecoder
VideoDecoder::VideoDecoder() {
av_frame_ = av_frame_alloc();
hw_frame_ = av_frame_alloc();
}
VideoDecoder::~VideoDecoder() {
if (hw_device_ctx) av_buffer_unref(&hw_device_ctx);
if (decoder_ctx) avcodec_free_context(&decoder_ctx);
av_frame_free(&av_frame_);
av_frame_free(&hw_frame_);
}
bool VideoDecoder::open(AVCodecParameters *codecpar, bool hw_decoder) {
const AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id);
if (!decoder) return false;
decoder_ctx = avcodec_alloc_context3(decoder);
if (!decoder_ctx || avcodec_parameters_to_context(decoder_ctx, codecpar) != 0) {
rError("Failed to allocate or initialize codec context");
return false;
}
width = (decoder_ctx->width + 3) & ~3;
height = decoder_ctx->height;
if (hw_decoder && !initHardwareDecoder(HW_DEVICE_TYPE)) {
rWarning("No device with hardware decoder found. fallback to CPU decoding.");
}
if (avcodec_open2(decoder_ctx, decoder, nullptr) < 0) {
rError("Failed to open codec");
return false;
}
return true;
}
bool VideoDecoder::initHardwareDecoder(AVHWDeviceType hw_device_type) {
const AVCodecHWConfig *config = nullptr;
for (int i = 0; (config = avcodec_get_hw_config(decoder_ctx->codec, i)) != nullptr; i++) {
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == hw_device_type) {
hw_pix_fmt = config->pix_fmt;
break;
}
}
if (!config) {
rWarning("Hardware configuration not found");
return false;
}
int ret = av_hwdevice_ctx_create(&hw_device_ctx, hw_device_type, nullptr, nullptr, 0);
if (ret < 0) {
hw_pix_fmt = AV_PIX_FMT_NONE;
has_hw_decoder = false;
rWarning("Failed to create specified HW device %d.", ret);
return false;
}
@@ -118,34 +167,27 @@ bool FrameReader::initHardwareDecoder(AVHWDeviceType hw_device_type) {
return true;
}
bool FrameReader::get(int idx, VisionBuf *buf) {
if (!buf || idx < 0 || idx >= packets_info.size()) {
return false;
}
return decode(idx, buf);
}
bool FrameReader::decode(int idx, VisionBuf *buf) {
bool VideoDecoder::decode(FrameReader *reader, int idx, VisionBuf *buf) {
int from_idx = idx;
if (idx != prev_idx + 1) {
if (idx != reader->prev_idx + 1) {
// seeking to the nearest key frame
for (int i = idx; i >= 0; --i) {
if (packets_info[i].flags & AV_PKT_FLAG_KEY) {
if (reader->packets_info[i].flags & AV_PKT_FLAG_KEY) {
from_idx = i;
break;
}
}
avio_seek(input_ctx->pb, packets_info[from_idx].pos, SEEK_SET);
avio_seek(reader->input_ctx->pb, reader->packets_info[from_idx].pos, SEEK_SET);
}
prev_idx = idx;
reader->prev_idx = idx;
bool result = false;
AVPacket pkt;
for (int i = from_idx; i <= idx; ++i) {
if (av_read_frame(input_ctx, &pkt) == 0) {
if (av_read_frame(reader->input_ctx, &pkt) == 0) {
AVFrame *f = decodeFrame(&pkt);
if (f && i == idx) {
result = copyBuffers(f, buf);
result = copyBuffer(f, buf);
}
av_packet_unref(&pkt);
}
@@ -153,33 +195,27 @@ bool FrameReader::decode(int idx, VisionBuf *buf) {
return result;
}
AVFrame *FrameReader::decodeFrame(AVPacket *pkt) {
AVFrame *VideoDecoder::decodeFrame(AVPacket *pkt) {
int ret = avcodec_send_packet(decoder_ctx, pkt);
if (ret < 0) {
rError("Error sending a packet for decoding: %d", ret);
return nullptr;
}
av_frame_.reset(av_frame_alloc());
ret = avcodec_receive_frame(decoder_ctx, av_frame_.get());
ret = avcodec_receive_frame(decoder_ctx, av_frame_);
if (ret != 0) {
rError("avcodec_receive_frame error: %d", ret);
return nullptr;
}
if (av_frame_->format == hw_pix_fmt) {
hw_frame.reset(av_frame_alloc());
if ((ret = av_hwframe_transfer_data(hw_frame.get(), av_frame_.get(), 0)) < 0) {
rError("error transferring the data from GPU to CPU");
return nullptr;
}
return hw_frame.get();
} else {
return av_frame_.get();
if (av_frame_->format == hw_pix_fmt && av_hwframe_transfer_data(hw_frame_, av_frame_, 0) < 0) {
rError("error transferring frame data from GPU to CPU");
return nullptr;
}
return (av_frame_->format == hw_pix_fmt) ? hw_frame_ : av_frame_;
}
bool FrameReader::copyBuffers(AVFrame *f, VisionBuf *buf) {
bool VideoDecoder::copyBuffer(AVFrame *f, VisionBuf *buf) {
if (hw_pix_fmt == HW_PIX_FMT) {
for (int i = 0; i < height/2; i++) {
memcpy(buf->y + (i*2 + 0)*buf->stride, f->data[0] + (i*2 + 0)*f->linesize[0], width);
+25 -18
View File
@@ -1,10 +1,10 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "cereal/visionipc/visionbuf.h"
#include "system/camerad/cameras/camera_common.h"
#include "tools/replay/filereader.h"
extern "C" {
@@ -12,39 +12,46 @@ extern "C" {
#include <libavformat/avformat.h>
}
struct AVFrameDeleter {
void operator()(AVFrame* frame) const { av_frame_free(&frame); }
};
class VideoDecoder;
class FrameReader {
public:
FrameReader();
~FrameReader();
bool load(const std::string &url, bool no_hw_decoder = false, std::atomic<bool> *abort = nullptr, bool local_cache = false,
bool load(CameraType type, const std::string &url, bool no_hw_decoder = false, std::atomic<bool> *abort = nullptr, bool local_cache = false,
int chunk_size = -1, int retries = 0);
bool loadFromFile(const std::string &file, bool no_hw_decoder = false, std::atomic<bool> *abort = nullptr);
bool loadFromFile(CameraType type, const std::string &file, bool no_hw_decoder = false, std::atomic<bool> *abort = nullptr);
bool get(int idx, VisionBuf *buf);
size_t getFrameCount() const { return packets_info.size(); }
int width = 0, height = 0;
private:
bool initHardwareDecoder(AVHWDeviceType hw_device_type);
bool decode(int idx, VisionBuf *buf);
AVFrame * decodeFrame(AVPacket *pkt);
bool copyBuffers(AVFrame *f, VisionBuf *buf);
std::unique_ptr<AVFrame, AVFrameDeleter>av_frame_, hw_frame;
VideoDecoder *decoder_ = nullptr;
AVFormatContext *input_ctx = nullptr;
AVCodecContext *decoder_ctx = nullptr;
AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE;
AVBufferRef *hw_device_ctx = nullptr;
int prev_idx = -1;
struct PacketInfo {
int flags;
int64_t pos;
};
std::vector<PacketInfo> packets_info;
inline static std::atomic<bool> has_hw_decoder = true;
};
class VideoDecoder {
public:
VideoDecoder();
~VideoDecoder();
bool open(AVCodecParameters *codecpar, bool hw_decoder);
bool decode(FrameReader *reader, int idx, VisionBuf *buf);
int width = 0, height = 0;
private:
bool initHardwareDecoder(AVHWDeviceType hw_device_type);
AVFrame *decodeFrame(AVPacket *pkt);
bool copyBuffer(AVFrame *f, VisionBuf *buf);
AVFrame *av_frame_, *hw_frame_;
AVCodecContext *decoder_ctx = nullptr;
AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE;
AVBufferRef *hw_device_ctx = nullptr;
};
+1 -1
View File
@@ -160,7 +160,7 @@ void Segment::loadFile(int id, const std::string file) {
bool success = false;
if (id < MAX_CAMERAS) {
frames[id] = std::make_unique<FrameReader>();
success = frames[id]->load(file, flags & REPLAY_FLAG_NO_HW_DECODER, &abort_, local_cache, 20 * 1024 * 1024, 3);
success = frames[id]->load((CameraType)id, file, flags & REPLAY_FLAG_NO_HW_DECODER, &abort_, local_cache, 20 * 1024 * 1024, 3);
} else {
log = std::make_unique<LogReader>(filters_);
success = log->load(file, &abort_, local_cache, 0, 3);