Merge branch 'master' into dev-priv/master

# Conflicts:
#	selfdrive/car/car_helpers.py
This commit is contained in:
Jason Wen
2023-02-17 01:28:52 -05:00
50 changed files with 508 additions and 323 deletions
+1
View File
@@ -7,6 +7,7 @@ Version 0.9.1 (2023-02-23)
* Adjust alert volume using ambient noise level
* Driver monitoring icon shows driver's head pose
* German translation thanks to Vrabetz and CzokNorris!
* Cadillac Escalade 2017 support thanks to rickygilleland!
* Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler!
* Genesis GV60 2023 support thanks to sunnyhaibin!
* Hyundai Tucson 2022-23 support
+1
View File
@@ -155,6 +155,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"NavSettingTime24h", PERSISTENT},
{"NavSettingLeftSide", PERSISTENT},
{"NavdRender", PERSISTENT},
{"ObdMultiplexingDisabled", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"OpenpilotEnabledToggle", PERSISTENT},
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"PandaSignatures", CLEAR_ON_MANAGER_START},
+2 -1
View File
@@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system.
# 236 Supported Cars
# 237 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@@ -17,6 +17,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=Q3 2019-23">J533</a>||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=RS3 2018">J533</a>||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,9</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Audi&model=S3 2015-17">J533</a>||
|Cadillac|Escalade 2017[<sup>3</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade 2017">OBD-II</a>||
|Cadillac|Escalade ESV 2016[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade ESV 2016">OBD-II</a>||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Bolt EUV 2022-23">GM</a>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Bolt EV 2022-23">GM</a>||
+2
View File
@@ -137,6 +137,8 @@ bool safety_setter_thread(std::vector<Panda *> pandas) {
panda->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U);
}
p.putBool("ObdMultiplexingDisabled", true);
std::string params;
LOGW("waiting for params to set safety model");
while (true) {
+2 -2
View File
@@ -11,7 +11,7 @@ from system.version import is_comma_remote, is_tested_branch
from selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from selfdrive.car.fw_versions import get_fw_versions_ordered, match_fw_to_car, get_present_ecus
from selfdrive.car.fw_versions import disable_obd_multiplexing, get_fw_versions_ordered, match_fw_to_car, get_present_ecus
import selfdrive.sentry as sentry
from system.swaglog import cloudlog
import cereal.messaging as messaging
@@ -120,7 +120,7 @@ def fingerprint(logcan, sendcan, num_pandas):
params = Params()
params.put("CarVin", vin)
params.put_bool("FirmwareObdQueryDone", True)
disable_obd_multiplexing(params)
finger = gen_empty_fingerprint()
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1
+2
View File
@@ -59,6 +59,8 @@ class Request:
bus: int = 1
# FW responses from these queries will not be used for fingerprinting
logging: bool = False
# These requests are done once OBD multiplexing is disabled, after all others
non_obd: bool = False
@dataclass
+24 -2
View File
@@ -5,6 +5,7 @@ from tqdm import tqdm
import panda.python.uds as uds
from cereal import car
from common.params import Params
from selfdrive.car.ecu_addrs import get_ecu_addrs
from selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.fingerprints import FW_VERSIONS
@@ -89,7 +90,7 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None):
return set()
def match_fw_to_car_exact(fw_versions_dict):
def match_fw_to_car_exact(fw_versions_dict) -> Set[str]:
"""Do an exact FW match. Returns all cars that match the given
FW versions for a list of "essential" ECUs. If an ECU is not considered
essential the FW version can be missing to get a fingerprint, but if it's present it
@@ -197,11 +198,21 @@ def get_brand_ecu_matches(ecu_rx_addrs):
return brand_matches
def disable_obd_multiplexing(params):
if not params.get_bool("ObdMultiplexingDisabled"):
params.put_bool("FirmwareObdQueryDone", True)
cloudlog.warning("Waiting for OBD multiplexing to be disabled")
params.get_bool("ObdMultiplexingDisabled", block=True)
cloudlog.warning("OBD multiplexing disabled")
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False):
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
all_car_fw = []
brand_matches = get_brand_ecu_matches(ecu_rx_addrs)
matched_brand: Optional[str] = None
for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True):
car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, debug=debug, progress=progress)
@@ -209,12 +220,20 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand
# Try to match using FW returned from this brand only
matches = match_fw_to_car_exact(build_fw_dict(car_fw))
if len(matches) == 1:
matched_brand = brand
break
disable_obd_multiplexing(Params())
# Do non-OBD queries for matched brand, or all if no match is found
for brand in FW_QUERY_CONFIGS.keys():
if brand == matched_brand or matched_brand is None:
all_car_fw.extend(get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, num_pandas=num_pandas, obd_multiplexed=False, debug=debug, progress=progress))
return all_car_fw
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False):
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, obd_multiplexed=True, debug=False, progress=False):
versions = VERSIONS.copy()
# Each brand can define extra ECUs to query for data collection
@@ -262,6 +281,9 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1,
# Skip query if no panda available
if r.bus > num_pandas * 4 - 1:
continue
# Or if request is not designated for current multiplexing mode
elif r.non_obd == obd_multiplexed:
continue
try:
addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and
+16 -9
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
import numpy as np
from cereal import car
from math import fabs
from panda import Panda
@@ -59,23 +58,23 @@ class CarInterface(CarInterfaceBase):
# TODO:
# 1. Learn the correction factors from data
# 2. Generalize the logic to other GM torque control platforms
steer_break_pts = np.array([-1.0, -0.9, -0.75, -0.5, 0.0, 0.5, 0.75, 0.9, 1.0])
steer_lataccel_factors = np.array([1.5, 1.15, 1.02, 1.0, 1.0, 1.0, 1.02, 1.15, 1.5])
steer_correction_factor = np.interp(
steer_break_pts = [-1.0, -0.9, -0.75, -0.5, 0.0, 0.5, 0.75, 0.9, 1.0]
steer_lataccel_factors = [1.5, 1.15, 1.02, 1.0, 1.0, 1.0, 1.02, 1.15, 1.5]
steer_correction_factor = interp(
steer_torque,
steer_break_pts,
steer_lataccel_factors
)
vego_break_pts = np.array([0.0, 10.0, 15.0, 20.0, 100.0])
vego_lataccel_factors = np.array([1.5, 1.5, 1.25, 1.0, 1.0])
vego_correction_factor = np.interp(
vego_break_pts = [0.0, 10.0, 15.0, 20.0, 100.0]
vego_lataccel_factors = [1.5, 1.5, 1.25, 1.0, 1.0]
vego_correction_factor = interp(
vego,
vego_break_pts,
vego_lataccel_factors,
)
return float((steer_torque + friction) / (steer_correction_factor * vego_correction_factor))
return (steer_torque + friction) / (steer_correction_factor * vego_correction_factor)
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
if self.CP.carFingerprint == CAR.BOLT_EUV:
@@ -199,6 +198,14 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 15.3
ret.centerToFront = ret.wheelbase * 0.5
elif candidate == CAR.ESCALADE:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
ret.mass = 5653. * CV.LB_TO_KG + STD_CARGO_KG # (5552+5815)/2
ret.wheelbase = 2.95 # 116 inches in meters
ret.steerRatio = 17.3
ret.centerToFront = ret.wheelbase * 0.5
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.ESCALADE_ESV:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
ret.mass = 2739. + STD_CARGO_KG
@@ -214,7 +221,7 @@ class CarInterface(CarInterfaceBase):
ret.mass = 1669. + STD_CARGO_KG
ret.wheelbase = 2.63779
ret.steerRatio = 16.8
ret.centerToFront = 2.15 # measured
ret.centerToFront = ret.wheelbase * 0.4
tire_stiffness_factor = 1.0
ret.steerActuatorDelay = 0.12
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
+6
View File
@@ -67,6 +67,7 @@ class CAR:
MALIBU = "CHEVROLET MALIBU PREMIER 2017"
ACADIA = "GMC ACADIA DENALI 2018"
BUICK_REGAL = "BUICK REGAL ESSENCE 2018"
ESCALADE = "CADILLAC ESCALADE 2017"
ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016"
BOLT_EUV = "CHEVROLET BOLT EUV 2022"
SILVERADO = "CHEVROLET SILVERADO 1500 2020"
@@ -99,6 +100,7 @@ CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = {
CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017"),
CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"),
CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"),
CAR.ESCALADE: GMCarInfo("Cadillac Escalade 2017", "Driver Assist Package"),
CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"),
CAR.BOLT_EUV: [
GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", "https://youtu.be/xvwzGMUA210"),
@@ -174,6 +176,10 @@ FINGERPRINTS = {
{
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 338: 6, 340: 6, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1919: 7, 1920: 7, 1930: 7, 2016: 8, 2024: 8
}],
CAR.ESCALADE: [
{
170: 8, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 4, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 460: 5, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1917: 7, 1918: 7, 1919: 7, 1920: 7, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
}],
CAR.ESCALADE_ESV: [
{
309: 1, 848: 8, 849: 8, 850: 8, 851: 8, 852: 8, 853: 8, 854: 3, 1056: 6, 1057: 8, 1058: 8, 1059: 8, 1060: 8, 1061: 8, 1062: 8, 1063: 8, 1064: 8, 1065: 8, 1066: 8, 1067: 8, 1068: 8, 1120: 8, 1121: 8, 1122: 8, 1123: 8, 1124: 8, 1125: 8, 1126: 8, 1127: 8, 1128: 8, 1129: 8, 1130: 8, 1131: 8, 1132: 8, 1133: 8, 1134: 8, 1135: 8, 1136: 8, 1137: 8, 1138: 8, 1139: 8, 1140: 8, 1141: 8, 1142: 8, 1143: 8, 1146: 8, 1147: 8, 1148: 8, 1149: 8, 1150: 8, 1151: 8, 1216: 8, 1217: 8, 1218: 8, 1219: 8, 1220: 8, 1221: 8, 1222: 8, 1223: 8, 1224: 8, 1225: 8, 1226: 8, 1232: 8, 1233: 8, 1234: 8, 1235: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1240: 8, 1241: 8, 1242: 8, 1787: 8, 1788: 8
+2
View File
@@ -22,6 +22,8 @@ class CarInterface(CarInterfaceBase):
def get_pid_accel_limits(CP, current_speed, cruise_speed):
if CP.carFingerprint in HONDA_BOSCH:
return CarControllerParams.BOSCH_ACCEL_MIN, CarControllerParams.BOSCH_ACCEL_MAX
elif CP.enableGasInterceptor:
return CarControllerParams.NIDEC_ACCEL_MIN, CarControllerParams.NIDEC_ACCEL_MAX
else:
# NIDECs don't allow acceleration near cruise_speed,
# so limit limits of pid to prevent windup
+11 -3
View File
@@ -171,14 +171,22 @@ FW_QUERY_CONFIG = FwQueryConfig(
[HONDA_VERSION_REQUEST],
[HONDA_VERSION_RESPONSE],
bus=1,
logging=True
logging=True,
),
# Query Nidec PT bus from camera for data collection
# Nidec PT bus
Request(
[StdQueries.UDS_VERSION_REQUEST],
[StdQueries.UDS_VERSION_RESPONSE],
bus=0,
logging=True
logging=True,
),
# Bosch PT bus
Request(
[StdQueries.UDS_VERSION_REQUEST],
[StdQueries.UDS_VERSION_RESPONSE],
bus=1,
logging=True,
non_obd=True,
),
],
extra_ecus=[
+2 -1
View File
@@ -190,7 +190,8 @@ class CarController:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
# send 25 messages at a time to increases the likelihood of resume being accepted
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP.carFingerprint)] * 25)
self.last_button_frame = self.frame
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
# TODO: unclear if this is needed
+4 -4
View File
@@ -140,8 +140,8 @@ class CarState(CarStateBase):
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if not self.CP.openpilotLongitudinalControl:
aeb_src = "FCA11" if self.CP.carFingerprint in FEATURES["use_fca"] else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.carFingerprint in FEATURES["use_fca"] else "AEB_CmdAct"
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
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
ret.stockFcw = aeb_warning and not aeb_braking
@@ -337,7 +337,7 @@ class CarState(CarStateBase):
("SCC12", 50),
]
if CP.carFingerprint in FEATURES["use_fca"]:
if CP.flags & HyundaiFlags.USE_FCA.value:
signals += [
("FCA_CmdAct", "FCA11"),
("CF_VSM_Warn", "FCA11"),
@@ -432,7 +432,7 @@ class CarState(CarStateBase):
("SCC12", 50),
]
if CP.carFingerprint in FEATURES["use_fca"]:
if CP.flags & HyundaiFlags.USE_FCA.value:
signals += [
("FCA_CmdAct", "FCA11"),
("CF_VSM_Warn", "FCA11"),
+4
View File
@@ -49,6 +49,10 @@ class CarInterface(CarInterfaceBase):
if 0x485 in fingerprint[2]:
ret.flags |= HyundaiFlags.SEND_LFA.value
# These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
if 0x38d in fingerprint[0] or 0x38d in fingerprint[2]:
ret.flags |= HyundaiFlags.USE_FCA.value
ret.steerActuatorDelay = 0.1 # Default delay
ret.steerLimitTimer = 0.4
tire_stiffness_factor = 1.
+10 -4
View File
@@ -61,6 +61,7 @@ class HyundaiFlags(IntFlag):
ENABLE_BLINKERS = 32
CANFD_ALT_GEARS_2 = 64
SEND_LFA = 128
USE_FCA = 256
SP_CAN_LFA_BTN = 128
@@ -439,13 +440,14 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x816H6F6051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6G6051\x00\x00\x00\x00\x00\x00\x00\x00',
b'\xf1\x816H6G5051\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x816U3J9051\000\000\xf1\0006U3H1_C2\000\0006U3J9051\000\000PAE0G16NL0\x82zT\xd2',
b'\xf1\x816U3J8051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J8051\x00\x00PAETG16UL0\x00\x00\x00\x00',
b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL2\xad\xeb\xabt',
b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL2\x00\x00\x00\x00',
b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL0\x00\x00\x00\x00',
b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00PAE0G16NL2\xad\xeb\xabt',
],
},
CAR.IONIQ_EV_2020: {
@@ -485,6 +487,7 @@ FW_VERSIONS = {
CAR.IONIQ_HEV_2022: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00AEhe SCC F-CUP 1.00 1.00 99110-G2600 ',
b'\xf1\x00AEhe SCC FHCUP 1.00 1.00 99110-G2600 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00AE MDPS C 1.00 1.01 56310G2510\x00 4APHC101',
@@ -497,6 +500,7 @@ FW_VERSIONS = {
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x816U3J9051\x00\x00\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00HAE0G16NL2\x00\x00\x00\x00',
b'\xf1\x006U3H1_C2\x00\x006U3J9051\x00\x00HAE0G16NL2\x96\xda\xd4\xee',
],
},
CAR.SONATA: {
@@ -684,12 +688,14 @@ FW_VERSIONS = {
},
CAR.SANTA_FE: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00TM__ SCC F-CUP 1.00 1.00 99110-S1210 ',
b'\xf1\x00TM__ SCC F-CUP 1.00 1.01 99110-S2000 ',
b'\xf1\x00TM__ SCC F-CUP 1.00 1.02 99110-S2000 ',
b'\xf1\x00TM__ SCC F-CUP 1.00 1.03 99110-S2000 ',
],
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00TM ESC \r 100\x18\x031 58910-S2650',
b'\xf1\x00TM ESC \r 105\x19\x05# 58910-S1500',
b'\xf1\x00TM ESC \r 103\x18\x11\x08 58910-S2650',
b'\xf1\x00TM ESC \r 104\x19\x07\x08 58910-S2650',
b'\xf1\x00TM ESC \x02 100\x18\x030 58910-S2600',
@@ -708,11 +714,14 @@ FW_VERSIONS = {
b'\xf1\x00TM MDPS C 1.00 1.00 56340-S2000 8409',
b'\xf1\x00TM MDPS C 1.00 1.00 56340-S2000 8A12',
b'\xf1\x00TM MDPS C 1.00 1.01 56340-S2000 9129',
b'\xf1\x00TM MDPS R 1.00 1.02 57700-S1100 4TMDP102'
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00TM MFC AT EUR LHD 1.00 1.01 99211-S1010 181207',
b'\xf1\x00TM MFC AT USA LHD 1.00 1.00 99211-S2000 180409',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00bcsh8p54 U833\x00\x00\x00\x00\x00\x00TTM4V22US3_<]\xf1',
b'\xf1\x006W351_C2\x00\x006W3E1051\x00\x00TTM4T20NS5\x00\x00\x00\x00',
b'\xf1\x87LBJSGA7082574HG0\x87www\x98\x88\x88\x88\x99\xaa\xb9\x9afw\x86gx\x99\xa7\x89co\xf8\xffvU_\xffR\xaf\xf1\x816W3C2051\x00\x00\xf1\x006W351_C2\x00\x006W3C2051\x00\x00TTM2T20NS1\x00\xa6\xe0\x91',
b'\xf1\x87LBKSGA0458404HG0vfvg\x87www\x89\x99\xa8\x99y\xaa\xa7\x9ax\x88\xa7\x88t_\xf9\xff\x86w\x8f\xff\x15x\xf1\x816W3C2051\x00\x00\xf1\x006W351_C2\x00\x006W3C2051\x00\x00TTM2T20NS1\x00\x00\x00\x00',
@@ -1652,9 +1661,6 @@ FEATURES = {
"use_cluster_gears": {CAR.ELANTRA, CAR.KONA},
"use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON},
"use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020},
# these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
"use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_OPTIMA_G4_FL},
}
CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN}
+1
View File
@@ -50,6 +50,7 @@ routes = [
CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA),
CarTestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL),
CarTestRoute("ef8f2185104d862e|2023-02-09--18-37-13", GM.ESCALADE),
CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV),
CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT),
CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV, segment=1),
+2 -1
View File
@@ -32,7 +32,8 @@ class TestCarInterfaces(unittest.TestCase):
self.assertGreater(car_params.mass, 1)
self.assertGreater(car_params.wheelbase, 0)
self.assertGreater(car_params.centerToFront, 0)
# centerToFront is center of gravity to front wheels, assert a reasonable range
self.assertTrue(car_params.wheelbase * 0.3 < car_params.centerToFront < car_params.wheelbase * 0.7)
self.assertGreater(car_params.maxLateralAccel, 0)
# Longitudinal sanity checks
+1
View File
@@ -26,6 +26,7 @@ COMMA BODY: [.nan, 1000, .nan]
RAM 1500 5TH GEN: [2.0, 2.0, 0.0]
RAM HD 5TH GEN: [1.4, 1.4, 0.0]
SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11]
CADILLAC ESCALADE 2017: [1.899999976158142, 1.842270016670227, 0.1120000034570694]
CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05]
CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112]
CHEVROLET EQUINOX 2019: [2.0, 2.0, 0.05]
+1
View File
@@ -72,6 +72,7 @@ class TestStartup(unittest.TestCase):
params.clear_all()
params.put_bool("Passive", False)
params.put_bool("OpenpilotEnabledToggle", True)
params.put_bool("ObdMultiplexingDisabled", True)
# Build capnn version of FW array
if fw_versions is not None:
+1 -1
View File
@@ -17,7 +17,7 @@ def get_fingerprint(lr):
for c in msg.can:
# read also msgs sent by EON on CAN bus 0x80 and filter out the
# addr with more than 11 bits
if c.src % 0x80 == 0 and c.address < 0x800:
if c.src % 0x80 == 0 and c.address < 0x800 and c.address not in (0x7df, 0x7e0, 0x7e8):
msgs[c.address] = len(c.dat)
# show CAN fingerprint
+1 -1
View File
@@ -22,7 +22,7 @@ while True:
for c in lc.can:
# read also msgs sent by EON on CAN bus 0x80 and filter out the
# addr with more than 11 bits
if c.src in [0, 2] and c.address < 0x800:
if c.src % 0x80 == 0 and c.address < 0x800 and c.address not in (0x7df, 0x7e0, 0x7e8):
msgs[c.address] = len(c.dat)
fingerprint = ', '.join("%d: %d" % v for v in sorted(msgs.items()))
+1 -1
View File
@@ -619,7 +619,7 @@ kj::ArrayPtr<capnp::byte> Localizer::get_message_bytes(MessageBuilder& msg_build
}
bool Localizer::is_gps_ok() {
return (this->kf->get_filter_time() - this->last_gps_msg) < 1.0;
return (this->kf->get_filter_time() - this->last_gps_msg) < 2.0;
}
bool Localizer::critical_services_valid(std::map<std::string, double> critical_services) {
@@ -407,6 +407,7 @@ def setup_env(simulation=False, CP=None, cfg=None, controlsState=None):
params.put_bool("WideCameraOnly", False)
params.put_bool("DisableLogging", False)
params.put_bool("UbloxAvailable", True)
params.put_bool("ObdMultiplexingDisabled", True)
os.environ["NO_RADAR_SLEEP"] = "1"
os.environ["REPLAY"] = "1"
+1 -1
View File
@@ -1 +1 @@
3e53ce81f1ce26409fdc4479e650ef5626130876
8883c476d5abc12b4b2949e04c6d7c0cd7c8b9fa
+21 -6
View File
@@ -42,6 +42,21 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) {
QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &BinaryView::refresh);
addShortcuts();
setWhatsThis(R"(
<b>Binary View</b><br/>
<!-- TODO: add descprition here -->
Shortcuts:<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 />
Open chart:
<span style="background-color:lightGray;color:gray"> c </span>,
<span style="background-color:lightGray;color:gray"> p </span>,
<span style="background-color:lightGray;color:gray"> g </span><br />
)");
}
void BinaryView::addShortcuts() {
@@ -94,7 +109,7 @@ void BinaryView::addShortcuts() {
QObject::connect(shortcut_plot_c, &QShortcut::activated, shortcut_plot, &QShortcut::activated);
QObject::connect(shortcut_plot, &QShortcut::activated, [=]{
if (hovered_sig != nullptr) {
emit showChart(model->msg_id, hovered_sig, true, false);
emit showChart(*model->msg_id, hovered_sig, true, false);
}
});
}
@@ -189,14 +204,14 @@ void BinaryView::leaveEvent(QEvent *event) {
QTableView::leaveEvent(event);
}
void BinaryView::setMessage(const QString &message_id) {
void BinaryView::setMessage(const MessageId &message_id) {
model->msg_id = message_id;
verticalScrollBar()->setValue(0);
refresh();
}
void BinaryView::refresh() {
if (model->msg_id.isEmpty()) return;
if (!model->msg_id) return;
clearSelection();
anchor_index = QModelIndex();
@@ -231,7 +246,7 @@ std::tuple<int, int, bool> BinaryView::getSelection(QModelIndex index) {
void BinaryViewModel::refresh() {
beginResetModel();
items.clear();
if ((dbc_msg = dbc()->msg(msg_id))) {
if ((dbc_msg = dbc()->msg(*msg_id))) {
row_count = dbc_msg->size;
items.resize(row_count * column_count);
for (auto sig : dbc_msg->getSignals()) {
@@ -250,7 +265,7 @@ void BinaryViewModel::refresh() {
}
}
} else {
row_count = can->lastMessage(msg_id).dat.size();
row_count = can->lastMessage(*msg_id).dat.size();
items.resize(row_count * column_count);
}
endResetModel();
@@ -259,7 +274,7 @@ void BinaryViewModel::refresh() {
void BinaryViewModel::updateState() {
auto prev_items = items;
const auto &last_msg = can->lastMessage(msg_id);
const auto &last_msg = can->lastMessage(*msg_id);
const auto &binary = last_msg.dat;
// data size may changed.
+5 -3
View File
@@ -1,5 +1,7 @@
#pragma once
#include <optional>
#include <QApplication>
#include <QList>
#include <QSet>
@@ -46,7 +48,7 @@ public:
};
std::vector<Item> items;
QString msg_id;
std::optional<MessageId> msg_id;
const DBCMsg *dbc_msg = nullptr;
int row_count = 0;
const int column_count = 9;
@@ -57,7 +59,7 @@ class BinaryView : public QTableView {
public:
BinaryView(QWidget *parent = nullptr);
void setMessage(const QString &message_id);
void setMessage(const MessageId &message_id);
void highlight(const Signal *sig);
QSet<const Signal*> getOverlappingSignals() const;
inline void updateState() { model->updateState(); }
@@ -70,7 +72,7 @@ signals:
void resizeSignal(const Signal *sig, int from, int size);
void removeSignal(const Signal *sig);
void editSignal(const Signal *origin_s, Signal &s);
void showChart(const QString &name, const Signal *sig, bool show, bool merge);
void showChart(const MessageId &id, const Signal *sig, bool show, bool merge);
private:
void addShortcuts();
+68 -87
View File
@@ -11,9 +11,11 @@
#include <QRubberBand>
#include <QPushButton>
#include <QToolBar>
#include <QToolButton>
#include <QToolTip>
#include <QtConcurrent>
const int MAX_COLUMN_COUNT = 4;
// ChartsWidget
ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
@@ -23,14 +25,17 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
QToolBar *toolbar = new QToolBar(tr("Charts"), this);
toolbar->setIconSize({16, 16});
QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), "");
new_plot_btn->setToolTip(tr("New Plot"));
QAction *new_plot_btn = toolbar->addAction(utils::icon("file-plus"), tr("New Plot"));
toolbar->addWidget(title_label = new QLabel());
title_label->setContentsMargins(0, 0, 12, 0);
columns_cb = new QComboBox(this);
columns_cb->addItems({"1", "2", "3", "4"});
columns_lb_action = toolbar->addWidget(new QLabel(tr("Columns:")));
columns_cb_action = toolbar->addWidget(columns_cb);
QMenu *menu = new QMenu(this);
for (int i = 0; i < MAX_COLUMN_COUNT; ++i) {
menu->addAction(tr("%1").arg(i + 1), [=]() { setColumnCount(i + 1); });
}
columns_action = toolbar->addAction("");
columns_action->setMenu(menu);
qobject_cast<QToolButton*>(toolbar->widgetForAction(columns_action))->setPopupMode(QToolButton::InstantPopup);
QLabel *stretch_label = new QLabel(this);
stretch_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
@@ -44,13 +49,10 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
range_slider->setPageStep(60); // 1 min
range_slider_action = toolbar->addWidget(range_slider);
reset_zoom_action = toolbar->addWidget(reset_zoom_btn = new QToolButton());
reset_zoom_btn->setIcon(utils::icon("zoom-out"));
reset_zoom_btn->setToolTip(tr("Reset zoom"));
reset_zoom_btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
reset_zoom_action = toolbar->addAction(utils::icon("zoom-out"), tr("Reset Zoom"));
qobject_cast<QToolButton*>(toolbar->widgetForAction(reset_zoom_action))->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
remove_all_btn = toolbar->addAction(utils::icon("x"), "");
remove_all_btn->setToolTip(tr("Remove all charts"));
remove_all_btn = toolbar->addAction(utils::icon("x"), tr("Remove all charts"));
dock_btn = toolbar->addAction("");
main_layout->addWidget(toolbar);
@@ -73,10 +75,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
// init settings
use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() >
QApplication::style()->standardPalette().color(QPalette::Background).value();
column_count = std::clamp(settings.chart_column_count, 1, columns_cb->count());
column_count = std::clamp(settings.chart_column_count, 1, MAX_COLUMN_COUNT);
max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60);
display_range = {0, max_chart_range};
columns_cb->setCurrentIndex(column_count - 1);
range_slider->setValue(max_chart_range);
updateToolBar();
@@ -86,14 +87,18 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange);
QObject::connect(new_plot_btn, &QAction::triggered, this, &ChartsWidget::newChart);
QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll);
QObject::connect(reset_zoom_btn, &QToolButton::clicked, this, &ChartsWidget::zoomReset);
QObject::connect(columns_cb, SIGNAL(activated(int)), SLOT(setColumnCount(int)));
QObject::connect(reset_zoom_action, &QAction::triggered, this, &ChartsWidget::zoomReset);
QObject::connect(&settings, &Settings::changed, this, &ChartsWidget::settingChanged);
QObject::connect(dock_btn, &QAction::triggered, [this]() {
emit dock(!docking);
docking = !docking;
updateToolBar();
});
setWhatsThis(tr(R"(
<b>Chart view</b><br />
<!-- TODO: add descprition here -->
)"));
}
void ChartsWidget::eventsMerged() {
@@ -162,11 +167,12 @@ void ChartsWidget::setMaxChartRange(int value) {
void ChartsWidget::updateToolBar() {
title_label->setText(tr("Charts: %1").arg(charts.size()));
columns_action->setText(tr("Column: %1").arg(column_count));
range_lb->setText(QString("Range: %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0')));
range_lb_action->setVisible(!is_zoomed);
range_slider_action->setVisible(!is_zoomed);
reset_zoom_action->setVisible(is_zoomed);
reset_zoom_btn->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : "");
reset_zoom_action->setText(is_zoomed ? tr("Zoomin: %1-%2").arg(zoomed_range.first, 0, 'f', 1).arg(zoomed_range.second, 0, 'f', 1) : "");
remove_all_btn->setEnabled(!charts.isEmpty());
dock_btn->setIcon(utils::icon(docking ? "arrow-up-right-square" : "arrow-down-left-square"));
dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts"));
@@ -180,7 +186,7 @@ void ChartsWidget::settingChanged() {
}
}
ChartView *ChartsWidget::findChart(const QString &id, const Signal *sig) {
ChartView *ChartsWidget::findChart(const MessageId &id, const Signal *sig) {
for (auto c : charts)
if (c->hasSeries(id, sig)) return c;
return nullptr;
@@ -203,7 +209,7 @@ ChartView *ChartsWidget::createChart() {
return chart;
}
void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bool merge) {
void ChartsWidget::showChart(const MessageId &id, const Signal *sig, bool show, bool merge) {
setUpdatesEnabled(false);
ChartView *chart = findChart(id, sig);
if (show && !chart) {
@@ -211,29 +217,29 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo
chart->addSeries(id, sig);
updateState();
} else if (!show && chart) {
chart->removeSeries(id, sig);
chart->removeIf([&](auto &s) { return s.msg_id == id && s.sig == sig; });
}
updateToolBar();
setUpdatesEnabled(true);
}
void ChartsWidget::setColumnCount(int n) {
n = std::clamp(n + 1, 1, columns_cb->count());
n = std::clamp(n, 1, MAX_COLUMN_COUNT);
if (column_count != n) {
column_count = settings.chart_column_count = n;
updateToolBar();
updateLayout();
}
}
void ChartsWidget::updateLayout() {
int n = columns_cb->count();
int n = MAX_COLUMN_COUNT;
for (; n > 1; --n) {
if ((n * CHART_MIN_WIDTH + (n - 1) * charts_layout->spacing()) < charts_layout->geometry().width()) break;
}
bool show_column_cb = n > 1;
columns_lb_action->setVisible(show_column_cb);
columns_cb_action->setVisible(show_column_cb);
columns_action->setVisible(show_column_cb);
n = std::min(column_count, n);
if (charts.size() != charts_layout->count() || n != current_column_count) {
@@ -305,7 +311,6 @@ bool ChartsWidget::eventFilter(QObject *obj, QEvent *event) {
ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
series_type = settings.chart_series_type == 0 ? QAbstractSeries::SeriesTypeLine : QAbstractSeries::SeriesTypeScatter;
QChart *chart = new QChart();
chart->setBackgroundVisible(false);
axis_x = new QValueAxis(this);
@@ -363,45 +368,40 @@ ChartView::ChartView(QWidget *parent) : QChartView(nullptr, parent) {
QObject::connect(remove_btn, &QToolButton::clicked, this, &ChartView::remove);
}
void ChartView::addSeries(const QString &msg_id, const Signal *sig) {
void ChartView::addSeries(const MessageId &msg_id, const Signal *sig) {
if (hasSeries(msg_id, sig)) return;
QXYSeries *series = createSeries(series_type, getColor(sig));
chart()->addSeries(series);
series->attachAxis(axis_x);
series->attachAxis(axis_y);
auto [source, address] = DBCManager::parseId(msg_id);
sigs.push_back({.msg_id = msg_id, .address = address, .source = source, .sig = sig, .series = series});
sigs.push_back({.msg_id = msg_id, .sig = sig, .series = series});
updateTitle();
updateSeries(sig);
updateSeriesPoints();
emit seriesAdded(msg_id, sig);
}
void ChartView::removeSeries(const QString &msg_id, const Signal *sig) {
auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.msg_id == msg_id && s.sig == sig; });
if (it != sigs.end()) {
it = removeItem(it);
}
}
bool ChartView::hasSeries(const QString &msg_id, const Signal *sig) const {
bool ChartView::hasSeries(const MessageId &msg_id, const Signal *sig) const {
return std::any_of(sigs.begin(), sigs.end(), [&](auto &s) { return s.msg_id == msg_id && s.sig == sig; });
}
QList<ChartView::SigItem>::iterator ChartView::removeItem(const QList<ChartView::SigItem>::iterator &it) {
chart()->removeSeries(it->series);
it->series->deleteLater();
QString msg_id = it->msg_id;
const Signal *sig = it->sig;
auto ret = sigs.erase(it);
emit seriesRemoved(msg_id, sig);
if (!sigs.isEmpty()) {
void ChartView::removeIf(std::function<bool(const SigItem &s)> predicate) {
int prev_size = sigs.size();
for (auto it = sigs.begin(); it != sigs.end(); /**/) {
if (predicate(*it)) {
chart()->removeSeries(it->series);
it->series->deleteLater();
auto msg_id = it->msg_id;
auto sig = it->sig;
it = sigs.erase(it);
emit seriesRemoved(msg_id, sig);
} else {
++it;
}
}
if (sigs.empty()) {
emit remove();
} else if (sigs.size() != prev_size) {
updateAxisY();
} else {
emit remove();
}
return ret;
}
void ChartView::signalUpdated(const Signal *sig) {
@@ -412,23 +412,11 @@ void ChartView::signalUpdated(const Signal *sig) {
}
}
void ChartView::signalRemoved(const Signal *sig) {
for (auto it = sigs.begin(); it != sigs.end(); /**/) {
it = (it->sig == sig) ? removeItem(it) : ++it;
}
}
void ChartView::msgUpdated(uint32_t address) {
if (std::any_of(sigs.begin(), sigs.end(), [=](auto &s) { return s.address == address; }))
if (std::any_of(sigs.begin(), sigs.end(), [=](auto &s) { return s.msg_id.address == address; }))
updateTitle();
}
void ChartView::msgRemoved(uint32_t address) {
for (auto it = sigs.begin(); it != sigs.end(); /**/) {
it = (it->address == address) ? removeItem(it) : ++it;
}
}
void ChartView::manageSeries() {
SeriesSelector dlg(tr("Mange Chart"), this);
for (auto &s : sigs) {
@@ -436,19 +424,12 @@ void ChartView::manageSeries() {
}
if (dlg.exec() == QDialog::Accepted) {
auto items = dlg.seletedItems();
if (items.isEmpty()) {
emit remove();
} else {
for (auto s : items) {
addSeries(s->msg_id, s->sig);
}
for (auto it = sigs.begin(); it != sigs.end(); /**/) {
bool exists = std::any_of(items.cbegin(), items.cend(), [&](auto &s) {
return s->msg_id == it->msg_id && s->sig == it->sig;
});
it = exists ? ++it : removeItem(it);
}
for (auto s : items) {
addSeries(s->msg_id, s->sig);
}
removeIf([&](auto &s) {
return std::none_of(items.cbegin(), items.cend(), [&](auto &it) { return s.msg_id == it->msg_id && s.sig == it->sig; });
});
}
}
@@ -478,7 +459,7 @@ void ChartView::updateTitle() {
}
for (auto &s : sigs) {
auto decoration = s.series->isVisible() ? "none" : "line-through";
s.series->setName(QString("<span style=\"text-decoration:%1\"><b>%2</b> <font color=\"gray\">%3 %4</font></span>").arg(decoration, s.sig->name.c_str(), msgName(s.msg_id), s.msg_id));
s.series->setName(QString("<span style=\"text-decoration:%1\"><b>%2</b> <font color=\"gray\">%3 %4</font></span>").arg(decoration, s.sig->name.c_str(), msgName(s.msg_id), s.msg_id.toString()));
}
}
@@ -503,7 +484,7 @@ void ChartView::updateSeriesPoints() {
int pixels_per_point = width() / num_points;
if (series_type == QAbstractSeries::SeriesTypeScatter) {
((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 1, 8));
((QScatterSeries *)s.series)->setMarkerSize(std::clamp(pixels_per_point / 3, 2, 8));
} else {
s.series->setPointsVisible(pixels_per_point > 20);
}
@@ -540,7 +521,7 @@ void ChartView::updateSeries(const Signal *sig, const std::vector<Event *> *even
for (auto it = chunk.first; it != chunk.second; ++it) {
if ((*it)->which == cereal::Event::Which::CAN) {
for (const auto &c : (*it)->event.getCan()) {
if (s.address == c.getAddress() && s.source == c.getSrc()) {
if (s.msg_id.address == c.getAddress() && s.msg_id.source == c.getSrc()) {
auto dat = c.getDat();
double value = get_raw_value((uint8_t *)dat.begin(), dat.size(), *s.sig);
double ts = ((*it)->mono_time / (double)1e9) - route_start_time; // seconds
@@ -698,7 +679,7 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
text_list.push_front(QString::number(chart()->mapToValue(pt).x(), 'f', 3));
QPointF tooltip_pt(pt.x() + 12, plot_area.top() - 20);
QToolTip::showText(mapToGlobal(tooltip_pt.toPoint()), pt.isNull() ? "" : text_list.join("<br />"), this, plot_area.toRect());
scene()->update();
scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
} else {
QToolTip::hideText();
}
@@ -795,6 +776,9 @@ QXYSeries *ChartView::createSeries(QAbstractSeries::SeriesType type, QColor colo
pen.setWidth(2.0 * qApp->devicePixelRatio());
series->setPen(pen);
#endif
chart()->addSeries(series);
series->attachAxis(axis_x);
series->attachAxis(axis_y);
return series;
}
@@ -809,9 +793,6 @@ void ChartView::setSeriesType(QAbstractSeries::SeriesType type) {
}
for (auto &s : sigs) {
auto series = createSeries(series_type, getColor(s.sig));
chart()->addSeries(series);
series->attachAxis(axis_x);
series->attachAxis(axis_y);
series->replace(s.vals);
s.series = series;
}
@@ -870,7 +851,7 @@ SeriesSelector::SeriesSelector(QString title, QWidget *parent) : QDialog(parent)
for (auto it = can->can_msgs.cbegin(); it != can->can_msgs.cend(); ++it) {
if (auto m = dbc()->msg(it.key())) {
msgs_combo->addItem(QString("%1 (%2)").arg(m->name).arg(it.key()), it.key());
msgs_combo->addItem(QString("%1 (%2)").arg(m->name).arg(it.key().toString()), QVariant::fromValue(it.key()));
}
}
msgs_combo->model()->sort(0);
@@ -895,7 +876,7 @@ void SeriesSelector::add(QListWidgetItem *item) {
void SeriesSelector::remove(QListWidgetItem *item) {
auto it = (ListItem *)item;
if (it->msg_id == msgs_combo->currentData().toString()) {
if (it->msg_id == msgs_combo->currentData().value<MessageId>()) {
addItemToList(available_list, it->msg_id, it->sig);
}
delete item;
@@ -904,7 +885,7 @@ void SeriesSelector::remove(QListWidgetItem *item) {
void SeriesSelector::updateAvailableList(int index) {
if (index == -1) return;
available_list->clear();
QString msg_id = msgs_combo->itemData(index).toString();
MessageId msg_id = msgs_combo->itemData(index).value<MessageId>();
auto selected_items = seletedItems();
for (auto &[name, s] : dbc()->msg(msg_id)->sigs) {
bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=&s](auto it) { return it->msg_id == msg_id && it->sig == sig; });
@@ -914,9 +895,9 @@ void SeriesSelector::updateAvailableList(int index) {
}
}
void SeriesSelector::addItemToList(QListWidget *parent, const QString id, const Signal *sig, bool show_msg_name) {
void SeriesSelector::addItemToList(QListWidget *parent, const MessageId id, const Signal *sig, bool show_msg_name) {
QString text = QString("<span style=\"color:%0;\">■ </span> %1").arg(getColor(sig).name(), sig->name.c_str());
if (show_msg_name) text += QString(" <font color=\"gray\">%0 %1</font>").arg(msgName(id), id);
if (show_msg_name) text += QString(" <font color=\"gray\">%0 %1</font>").arg(msgName(id), id.toString());
QLabel *label = new QLabel(text);
label->setContentsMargins(5, 0, 5, 0);
+16 -24
View File
@@ -1,6 +1,5 @@
#pragma once
#include <QComboBox>
#include <QDragEnterEvent>
#include <QGridLayout>
#include <QLabel>
@@ -8,7 +7,6 @@
#include <QGraphicsPixmapItem>
#include <QGraphicsProxyWidget>
#include <QSlider>
#include <QToolButton>
#include <QtCharts/QChartView>
#include <QtCharts/QLegendMarker>
#include <QtCharts/QLineSeries>
@@ -27,18 +25,15 @@ class ChartView : public QChartView {
public:
ChartView(QWidget *parent = nullptr);
void addSeries(const QString &msg_id, const Signal *sig);
void removeSeries(const QString &msg_id, const Signal *sig);
bool hasSeries(const QString &msg_id, const Signal *sig) const;
void addSeries(const MessageId &msg_id, const Signal *sig);
bool hasSeries(const MessageId &msg_id, const Signal *sig) const;
void updateSeries(const Signal *sig = nullptr, const std::vector<Event*> *events = nullptr, bool clear = true);
void updatePlot(double cur, double min, double max);
void setSeriesType(QAbstractSeries::SeriesType type);
void updatePlotArea(int left);
struct SigItem {
QString msg_id;
uint8_t source = 0;
uint32_t address = 0;
MessageId msg_id;
const Signal *sig = nullptr;
QXYSeries *series = nullptr;
QVector<QPointF> vals;
@@ -46,23 +41,22 @@ public:
};
signals:
void seriesRemoved(const QString &id, const Signal *sig);
void seriesAdded(const QString &id, const Signal *sig);
void seriesRemoved(const MessageId &id, const Signal *sig);
void seriesAdded(const MessageId &id, const Signal *sig);
void zoomIn(double min, double max);
void zoomReset();
void remove();
void axisYLabelWidthChanged(int w);
private slots:
void msgRemoved(uint32_t address);
void msgUpdated(uint32_t address);
void signalUpdated(const Signal *sig);
void signalRemoved(const Signal *sig);
void manageSeries();
void handleMarkerClicked();
void msgRemoved(uint32_t address) { removeIf([=](auto &s) { return s.msg_id.address == address; }); }
void signalRemoved(const Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); }
private:
QList<ChartView::SigItem>::iterator removeItem(const QList<ChartView::SigItem>::iterator &it);
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *ev) override;
@@ -78,6 +72,7 @@ private:
qreal niceNumber(qreal x, bool ceiling);
QXYSeries *createSeries(QAbstractSeries::SeriesType type, QColor color);
void updateSeriesPoints();
void removeIf(std::function<bool(const SigItem &)> predicate);
int y_label_width = 0;
int align_to = 0;
@@ -102,8 +97,8 @@ class ChartsWidget : public QWidget {
public:
ChartsWidget(QWidget *parent = nullptr);
void showChart(const QString &id, const Signal *sig, bool show, bool merge);
inline bool hasSignal(const QString &id, const Signal *sig) { return findChart(id, sig) != nullptr; }
void showChart(const MessageId &id, const Signal *sig, bool show, bool merge);
inline bool hasSignal(const MessageId &id, const Signal *sig) { return findChart(id, sig) != nullptr; }
public slots:
void setColumnCount(int n);
@@ -129,7 +124,7 @@ private:
void updateLayout();
void settingChanged();
bool eventFilter(QObject *obj, QEvent *event) override;
ChartView *findChart(const QString &id, const Signal *sig);
ChartView *findChart(const MessageId &id, const Signal *sig);
QLabel *title_label;
QLabel *range_lb;
@@ -139,7 +134,6 @@ private:
bool docking = true;
QAction *dock_btn;
QAction *reset_zoom_action;
QToolButton *reset_zoom_btn;
QAction *remove_all_btn;
QGridLayout *charts_layout;
QList<ChartView *> charts;
@@ -148,9 +142,7 @@ private:
std::pair<double, double> display_range;
std::pair<double, double> zoomed_range;
bool use_dark_theme = false;
QAction *columns_lb_action;
QAction *columns_cb_action;
QComboBox *columns_cb;
QAction *columns_action;
int column_count = 1;
int current_column_count = 0;
};
@@ -158,18 +150,18 @@ private:
class SeriesSelector : public QDialog {
public:
struct ListItem : public QListWidgetItem {
ListItem(const QString &msg_id, const Signal *sig, QListWidget *parent) : msg_id(msg_id), sig(sig), QListWidgetItem(parent) {}
QString msg_id;
ListItem(const MessageId &msg_id, const Signal *sig, QListWidget *parent) : msg_id(msg_id), sig(sig), QListWidgetItem(parent) {}
MessageId msg_id;
const Signal *sig;
};
SeriesSelector(QString title, QWidget *parent);
QList<ListItem *> seletedItems();
inline void addSelected(const QString &id, const Signal *sig) { addItemToList(selected_list, id, sig, true); }
inline void addSelected(const MessageId &id, const Signal *sig) { addItemToList(selected_list, id, sig, true); }
private:
void updateAvailableList(int index);
void addItemToList(QListWidget *parent, const QString id, const Signal *sig, bool show_msg_name = false);
void addItemToList(QListWidget *parent, const MessageId id, const Signal *sig, bool show_msg_name = false);
void add(QListWidgetItem *item);
void remove(QListWidgetItem *item);
+9 -9
View File
@@ -4,13 +4,13 @@
// EditMsgCommand
EditMsgCommand::EditMsgCommand(const QString &id, const QString &title, int size, QUndoCommand *parent)
EditMsgCommand::EditMsgCommand(const MessageId &id, const QString &title, int size, QUndoCommand *parent)
: id(id), new_title(title), new_size(size), QUndoCommand(parent) {
if (auto msg = dbc()->msg(id)) {
old_title = msg->name;
old_size = msg->size;
}
setText(QObject::tr("Edit message %1:%2").arg(DBCManager::parseId(id).second).arg(title));
setText(QObject::tr("Edit message %1:%2").arg(id.address).arg(title));
}
void EditMsgCommand::undo() {
@@ -26,10 +26,10 @@ void EditMsgCommand::redo() {
// RemoveMsgCommand
RemoveMsgCommand::RemoveMsgCommand(const QString &id, QUndoCommand *parent) : id(id), QUndoCommand(parent) {
RemoveMsgCommand::RemoveMsgCommand(const MessageId &id, QUndoCommand *parent) : id(id), QUndoCommand(parent) {
if (auto msg = dbc()->msg(id)) {
message = *msg;
setText(QObject::tr("Remove message %1:%2").arg(DBCManager::parseId(id).second).arg(message.name));
setText(QObject::tr("Remove message %1:%2").arg(id.address).arg(message.name));
}
}
@@ -48,9 +48,9 @@ void RemoveMsgCommand::redo() {
// AddSigCommand
AddSigCommand::AddSigCommand(const QString &id, const Signal &sig, QUndoCommand *parent)
AddSigCommand::AddSigCommand(const MessageId &id, const Signal &sig, QUndoCommand *parent)
: id(id), signal(sig), QUndoCommand(parent) {
setText(QObject::tr("Add signal %1 to %2").arg(sig.name.c_str()).arg(DBCManager::parseId(id).second));
setText(QObject::tr("Add signal %1 to %2").arg(sig.name.c_str()).arg(id.address));
}
void AddSigCommand::undo() { dbc()->removeSignal(id, signal.name.c_str()); }
@@ -58,9 +58,9 @@ void AddSigCommand::redo() { dbc()->addSignal(id, signal); }
// RemoveSigCommand
RemoveSigCommand::RemoveSigCommand(const QString &id, const Signal *sig, QUndoCommand *parent)
RemoveSigCommand::RemoveSigCommand(const MessageId &id, const Signal *sig, QUndoCommand *parent)
: id(id), signal(*sig), QUndoCommand(parent) {
setText(QObject::tr("Remove signal %1 from %2").arg(signal.name.c_str()).arg(DBCManager::parseId(id).second));
setText(QObject::tr("Remove signal %1 from %2").arg(signal.name.c_str()).arg(id.address));
}
void RemoveSigCommand::undo() { dbc()->addSignal(id, signal); }
@@ -68,7 +68,7 @@ void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name.c_str()); }
// EditSignalCommand
EditSignalCommand::EditSignalCommand(const QString &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent)
EditSignalCommand::EditSignalCommand(const MessageId &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent)
: id(id), old_signal(*sig), new_signal(new_sig), QUndoCommand(parent) {
setText(QObject::tr("Edit signal %1").arg(old_signal.name.c_str()));
}
+10 -10
View File
@@ -7,57 +7,57 @@
class EditMsgCommand : public QUndoCommand {
public:
EditMsgCommand(const QString &id, const QString &title, int size, QUndoCommand *parent = nullptr);
EditMsgCommand(const MessageId &id, const QString &title, int size, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
const QString id;
const MessageId id;
QString old_title, new_title;
int old_size = 0, new_size = 0;
};
class RemoveMsgCommand : public QUndoCommand {
public:
RemoveMsgCommand(const QString &id, QUndoCommand *parent = nullptr);
RemoveMsgCommand(const MessageId &id, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
const QString id;
const MessageId id;
DBCMsg message;
};
class AddSigCommand : public QUndoCommand {
public:
AddSigCommand(const QString &id, const Signal &sig, QUndoCommand *parent = nullptr);
AddSigCommand(const MessageId &id, const Signal &sig, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
const QString id;
const MessageId id;
Signal signal = {};
};
class RemoveSigCommand : public QUndoCommand {
public:
RemoveSigCommand(const QString &id, const Signal *sig, QUndoCommand *parent = nullptr);
RemoveSigCommand(const MessageId &id, const Signal *sig, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
const QString id;
const MessageId id;
Signal signal = {};
};
class EditSignalCommand : public QUndoCommand {
public:
EditSignalCommand(const QString &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent = nullptr);
EditSignalCommand(const MessageId &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
const QString id;
const MessageId id;
Signal old_signal = {};
Signal new_signal = {};
};
+15 -19
View File
@@ -4,6 +4,10 @@
#include <sstream>
#include <QVector>
uint qHash(const MessageId &item) {
return qHash(item.source) ^ qHash(item.address);
}
DBCManager::DBCManager(QObject *parent) : QObject(parent) {}
DBCManager::~DBCManager() {}
@@ -56,29 +60,27 @@ QString DBCManager::generateDBC() {
return dbc_string;
}
void DBCManager::updateMsg(const QString &id, const QString &name, uint32_t size) {
auto [_, address] = parseId(id);
auto &m = msgs[address];
void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size) {
auto &m = msgs[id.address];
m.name = name;
m.size = size;
emit msgUpdated(address);
emit msgUpdated(id.address);
}
void DBCManager::removeMsg(const QString &id) {
uint32_t address = parseId(id).second;
msgs.erase(address);
emit msgRemoved(address);
void DBCManager::removeMsg(const MessageId &id) {
msgs.erase(id.address);
emit msgRemoved(id.address);
}
void DBCManager::addSignal(const QString &id, const Signal &sig) {
if (auto m = const_cast<DBCMsg *>(msg(id))) {
void DBCManager::addSignal(const MessageId &id, const Signal &sig) {
if (auto m = const_cast<DBCMsg *>(msg(id.address))) {
auto &s = m->sigs[sig.name.c_str()];
s = sig;
emit signalAdded(parseId(id).second, &s);
emit signalAdded(id.address, &s);
}
}
void DBCManager::updateSignal(const QString &id, const QString &sig_name, const Signal &sig) {
void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const Signal &sig) {
if (auto m = const_cast<DBCMsg *>(msg(id))) {
// change key name
QString new_name = QString::fromStdString(sig.name);
@@ -91,7 +93,7 @@ void DBCManager::updateSignal(const QString &id, const QString &sig_name, const
}
}
void DBCManager::removeSignal(const QString &id, const QString &sig_name) {
void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) {
if (auto m = const_cast<DBCMsg *>(msg(id))) {
auto it = m->sigs.find(sig_name);
if (it != m->sigs.end()) {
@@ -101,12 +103,6 @@ void DBCManager::removeSignal(const QString &id, const QString &sig_name) {
}
}
std::pair<uint8_t, uint32_t> DBCManager::parseId(const QString &id) {
const auto list = id.split(':');
if (list.size() != 2) return {0, 0};
return {list[0].toInt(), list[1].toUInt(nullptr, 16)};
}
DBCManager *dbc() {
static DBCManager dbc_manager(nullptr);
return &dbc_manager;
+36 -8
View File
@@ -5,6 +5,35 @@
#include <QString>
#include "opendbc/can/common_dbc.h"
struct MessageId {
uint8_t source;
uint32_t address;
QString toString() const {
return QString("%1:%2").arg(source).arg(address, 1, 16);
}
bool operator==(const MessageId &other) const {
return source == other.source && address == other.address;
}
bool operator!=(const MessageId &other) const {
return !(*this == other);
}
bool operator<(const MessageId &other) const {
return std::pair{source, address} < std::pair{other.source, other.address};
}
bool operator>(const MessageId &other) const {
return std::pair{source, address} > std::pair{other.source, other.address};
}
};
Q_DECLARE_METATYPE(MessageId);
uint qHash(const MessageId &item);
struct DBCMsg {
QString name;
uint32_t size;
@@ -24,17 +53,16 @@ public:
void open(const QString &dbc_file_name);
bool open(const QString &name, const QString &content, QString *error = nullptr);
QString generateDBC();
void addSignal(const QString &id, const Signal &sig);
void updateSignal(const QString &id, const QString &sig_name, const Signal &sig);
void removeSignal(const QString &id, const QString &sig_name);
void addSignal(const MessageId &id, const Signal &sig);
void updateSignal(const MessageId &id, const QString &sig_name, const Signal &sig);
void removeSignal(const MessageId &id, const QString &sig_name);
static std::pair<uint8_t, uint32_t> parseId(const QString &id);
inline static std::vector<std::string> allDBCNames() { return get_dbc_names(); }
inline QString name() const { return dbc ? dbc->name.c_str() : ""; }
void updateMsg(const QString &id, const QString &name, uint32_t size);
void removeMsg(const QString &id);
void updateMsg(const MessageId &id, const QString &name, uint32_t size);
void removeMsg(const MessageId &id);
inline const std::map<uint32_t, DBCMsg> &messages() const { return msgs; }
inline const DBCMsg *msg(const QString &id) const { return msg(parseId(id).second); }
inline const DBCMsg *msg(const MessageId &id) const { return msg(id.address); }
inline const DBCMsg *msg(uint32_t address) const {
auto it = msgs.find(address);
return it != msgs.end() ? &it->second : nullptr;
@@ -65,7 +93,7 @@ int bigEndianBitIndex(int index);
void updateSigSizeParamsFromRange(Signal &s, int start_bit, int size);
std::pair<int, int> getSignalRange(const Signal *s);
DBCManager *dbc();
inline QString msgName(const QString &id) {
inline QString msgName(const MessageId &id) {
auto msg = dbc()->msg(id);
return msg ? msg->name : UNTITLED;
}
+37 -25
View File
@@ -85,11 +85,15 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &DetailWidget::refresh);
QObject::connect(tabbar, &QTabBar::customContextMenuRequested, this, &DetailWidget::showTabBarContextMenu);
QObject::connect(tabbar, &QTabBar::currentChanged, [this](int index) {
if (index != -1 && tabbar->tabText(index) != msg_id) {
setMessage(tabbar->tabText(index));
if (index != -1) {
setMessage(tabbar_ids[index]);
}
});
QObject::connect(tabbar, &QTabBar::tabCloseRequested, tabbar, &QTabBar::removeTab);
QObject::connect(tabbar, &QTabBar::tabCloseRequested, [this](int index) {
tabbar_ids.removeAt(index);
tabbar->removeTab(index);
assert(tabbar_ids.size() == tabbar->count());
});
QObject::connect(charts, &ChartsWidget::seriesChanged, signal_view, &SignalView::updateChartState);
}
@@ -99,38 +103,45 @@ void DetailWidget::showTabBarContextMenu(const QPoint &pt) {
QMenu menu(this);
menu.addAction(tr("Close Other Tabs"));
if (menu.exec(tabbar->mapToGlobal(pt))) {
tabbar_ids.move(index, 0);
tabbar->moveTab(index, 0);
tabbar->setCurrentIndex(0);
while (tabbar->count() > 1)
while (tabbar->count() > 1) {
tabbar_ids.removeAt(1);
tabbar->removeTab(1);
}
assert(tabbar_ids.size() == tabbar->count());
}
}
}
void DetailWidget::removeAll() {
msg_id = "";
msg_id = std::nullopt;
tabbar->blockSignals(true);
while (tabbar->count() > 0) {
tabbar->removeTab(0);
}
tabbar_ids.clear();
tabbar->blockSignals(false);
stacked_layout->setCurrentIndex(0);
}
void DetailWidget::setMessage(const QString &message_id) {
void DetailWidget::setMessage(const MessageId &message_id) {
msg_id = message_id;
int index = tabbar->count() - 1;
for (/**/; index >= 0 && tabbar->tabText(index) != msg_id; --index) { /**/ }
int index = tabbar_ids.indexOf(*msg_id);
if (index == -1) {
index = tabbar->addTab(message_id);
tabbar_ids.append(*msg_id);
index = tabbar->addTab(message_id.toString());
tabbar->setTabToolTip(index, msgName(message_id));
}
assert(tabbar->count() == tabbar_ids.size());
setUpdatesEnabled(false);
signal_view->setMessage(msg_id);
binary_view->setMessage(msg_id);
history_log->setMessage(msg_id);
signal_view->setMessage(*msg_id);
binary_view->setMessage(*msg_id);
history_log->setMessage(*msg_id);
stacked_layout->setCurrentIndex(1);
tabbar->setCurrentIndex(index);
@@ -141,12 +152,12 @@ void DetailWidget::setMessage(const QString &message_id) {
}
void DetailWidget::refresh() {
if (msg_id.isEmpty()) return;
if (!msg_id) return;
QStringList warnings;
const DBCMsg *msg = dbc()->msg(msg_id);
const DBCMsg *msg = dbc()->msg(*msg_id);
if (msg) {
if (msg->size != can->lastMessage(msg_id).dat.size()) {
if (msg->size != can->lastMessage(*msg_id).dat.size()) {
warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size));
}
for (auto s : binary_view->getOverlappingSignals()) {
@@ -156,7 +167,7 @@ void DetailWidget::refresh() {
warnings.push_back(tr("Drag-Select in binary view to create new signal."));
}
remove_msg_act->setEnabled(msg != nullptr);
name_label->setText(msgName(msg_id));
name_label->setText(msgName(*msg_id));
if (!warnings.isEmpty()) {
warning_label->setText(warnings.join('\n'));
@@ -165,9 +176,9 @@ void DetailWidget::refresh() {
warning_widget->setVisible(!warnings.isEmpty());
}
void DetailWidget::updateState(const QHash<QString, CanData> *msgs) {
void DetailWidget::updateState(const QHash<MessageId, CanData> *msgs) {
time_label->setText(QString::number(can->currentSec(), 'f', 3));
if (msg_id.isEmpty() || (msgs && !msgs->contains(msg_id)))
if (!msg_id || (msgs && !msgs->contains(*msg_id)))
return;
if (tab_widget->currentIndex() == 0)
@@ -177,24 +188,24 @@ void DetailWidget::updateState(const QHash<QString, CanData> *msgs) {
}
void DetailWidget::editMsg() {
QString id = msg_id;
MessageId id = *msg_id;
auto msg = dbc()->msg(id);
int size = msg ? msg->size : can->lastMessage(id).dat.size();
EditMessageDialog dlg(id, msgName(id), size, this);
if (dlg.exec()) {
UndoStack::push(new EditMsgCommand(msg_id, dlg.name_edit->text(), dlg.size_spin->value()));
UndoStack::push(new EditMsgCommand(*msg_id, dlg.name_edit->text(), dlg.size_spin->value()));
}
}
void DetailWidget::removeMsg() {
UndoStack::push(new RemoveMsgCommand(msg_id));
UndoStack::push(new RemoveMsgCommand(*msg_id));
}
// EditMessageDialog
EditMessageDialog::EditMessageDialog(const QString &msg_id, const QString &title, int size, QWidget *parent)
EditMessageDialog::EditMessageDialog(const MessageId &msg_id, const QString &title, int size, QWidget *parent)
: original_name(title), QDialog(parent) {
setWindowTitle(tr("Edit message: %1").arg(msg_id));
setWindowTitle(tr("Edit message: %1").arg(msg_id.toString()));
QFormLayout *form_layout = new QFormLayout(this);
form_layout->addRow("", error_label = new QLabel);
@@ -253,11 +264,12 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
return hlayout;
};
auto lb = new QLabel(tr("<-Select a message to to view details"));
auto lb = new QLabel(tr("<-Select a message to view details"));
lb->setAlignment(Qt::AlignHCenter);
main_layout->addWidget(lb);
main_layout->addLayout(newShortcutRow("Pause", "Space"));
main_layout->addLayout(newShortcutRow("Help", "Alt + H"));
main_layout->addLayout(newShortcutRow("Help", "F1"));
main_layout->addLayout(newShortcutRow("WhatsThis", "Shift+F1"));
main_layout->addStretch(0);
setStyleSheet("QLabel{color:darkGray;}");
+5 -4
View File
@@ -14,7 +14,7 @@
class EditMessageDialog : public QDialog {
public:
EditMessageDialog(const QString &msg_id, const QString &title, int size, QWidget *parent);
EditMessageDialog(const MessageId &msg_id, const QString &title, int size, QWidget *parent);
void validateName(const QString &text);
QString original_name;
@@ -34,7 +34,7 @@ class DetailWidget : public QWidget {
public:
DetailWidget(ChartsWidget *charts, QWidget *parent);
void setMessage(const QString &message_id);
void setMessage(const MessageId &message_id);
void refresh();
void removeAll();
QSize minimumSizeHint() const override { return binary_view->minimumSizeHint(); }
@@ -43,13 +43,14 @@ private:
void showTabBarContextMenu(const QPoint &pt);
void editMsg();
void removeMsg();
void updateState(const QHash<QString, CanData> * msgs = nullptr);
void updateState(const QHash<MessageId, CanData> * msgs = nullptr);
QString msg_id;
std::optional<MessageId> msg_id;
QLabel *time_label, *warning_icon, *warning_label;
ElidedLabel *name_label;
QWidget *warning_widget;
QTabBar *tabbar;
QList<MessageId> tabbar_ids;
QTabWidget *tab_widget;
QAction *remove_msg_act;
LogsWidget *history_log;
+9 -10
View File
@@ -17,19 +17,19 @@ QVariant HistoryLogModel::data(const QModelIndex &index, int role) const {
}
return show_signals ? QString::number(m.sig_values[index.column() - 1]) : toHex(m.data);
} else if (role == Qt::UserRole && index.column() == 1 && !show_signals) {
return ChangeTracker::toVariantList(m.colors);
return QVariant::fromValue(m.colors);
}
return {};
}
void HistoryLogModel::setMessage(const QString &message_id) {
void HistoryLogModel::setMessage(const MessageId &message_id) {
msg_id = message_id;
}
void HistoryLogModel::refresh() {
beginResetModel();
sigs.clear();
if (auto dbc_msg = dbc()->msg(msg_id)) {
if (auto dbc_msg = dbc()->msg(*msg_id)) {
sigs = dbc_msg->getSignals();
}
last_fetch_time = 0;
@@ -78,8 +78,8 @@ void HistoryLogModel::setFilter(int sig_idx, const QString &value, std::function
}
void HistoryLogModel::updateState() {
if (!msg_id.isEmpty()) {
uint64_t current_time = (can->lastMessage(msg_id).ts + can->routeStartTime()) * 1e9 + 1;
if (msg_id) {
uint64_t current_time = (can->lastMessage(*msg_id).ts + can->routeStartTime()) * 1e9 + 1;
auto new_msgs = dynamic_mode ? fetchData(current_time, last_fetch_time) : fetchData(0);
if (!new_msgs.empty()) {
beginInsertRows({}, 0, new_msgs.size() - 1);
@@ -106,12 +106,11 @@ void HistoryLogModel::fetchMore(const QModelIndex &parent) {
template <class InputIt>
std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData(InputIt first, InputIt last, uint64_t min_time) {
std::deque<HistoryLogModel::Message> msgs;
const auto [src, address] = DBCManager::parseId(msg_id);
QVector<double> values(sigs.size());
for (auto it = first; it != last && (*it)->mono_time > min_time; ++it) {
if ((*it)->which == cereal::Event::Which::CAN) {
for (const auto &c : (*it)->event.getCan()) {
if (address == c.getAddress() && src == c.getSrc()) {
if (msg_id->address == c.getAddress() && msg_id->source == c.getSrc()) {
const auto dat = c.getDat();
for (int i = 0; i < sigs.size(); ++i) {
values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), *(sigs[i]));
@@ -136,7 +135,7 @@ template std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData<>(std::
std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData(uint64_t from_time, uint64_t min_time) {
auto events = can->events();
const auto freq = can->lastMessage(msg_id).freq;
const auto freq = can->lastMessage(*msg_id).freq;
const bool update_colors = !display_signals_mode || sigs.empty();
if (dynamic_mode) {
@@ -241,13 +240,13 @@ LogsWidget::LogsWidget(QWidget *parent) : QWidget(parent) {
QObject::connect(can, &AbstractStream::eventsMerged, model, &HistoryLogModel::segmentsMerged);
}
void LogsWidget::setMessage(const QString &message_id) {
void LogsWidget::setMessage(const MessageId &message_id) {
model->setMessage(message_id);
refresh();
}
void LogsWidget::refresh() {
if (model->msg_id.isEmpty()) return;
if (!model->msg_id) return;
model->setFilter(0, "", nullptr);
model->refresh();
+5 -3
View File
@@ -1,6 +1,8 @@
#pragma once
#include <deque>
#include <optional>
#include <QCheckBox>
#include <QComboBox>
#include <QHeaderView>
@@ -22,7 +24,7 @@ class HistoryLogModel : public QAbstractTableModel {
public:
HistoryLogModel(QObject *parent) : QAbstractTableModel(parent) {}
void setMessage(const QString &message_id);
void setMessage(const MessageId &message_id);
void updateState();
void setFilter(int sig_idx, const QString &value, std::function<bool(double, double)> cmp);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
@@ -52,7 +54,7 @@ public:
std::deque<HistoryLogModel::Message> fetchData(InputIt first, InputIt last, uint64_t min_time);
std::deque<Message> fetchData(uint64_t from_time, uint64_t min_time = 0);
QString msg_id;
std::optional<MessageId> msg_id;
ChangeTracker hex_colors;
bool has_more_data = true;
const int batch_size = 50;
@@ -71,7 +73,7 @@ class LogsWidget : public QWidget {
public:
LogsWidget(QWidget *parent);
void setMessage(const QString &message_id);
void setMessage(const MessageId &message_id);
void updateState() {if (dynamic_mode->isChecked()) model->updateState(); }
void showEvent(QShowEvent *event) override { if (dynamic_mode->isChecked()) model->refresh(); }
+64 -2
View File
@@ -9,7 +9,9 @@
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QResizeEvent>
#include <QShortcut>
#include <QTextDocument>
#include <QUndoView>
#include <QVBoxLayout>
#include <QWidgetAction>
@@ -20,7 +22,7 @@
static MainWindow *main_win = nullptr;
void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
if (type == QtDebugMsg) std::cout << msg.toStdString() << std::endl;
if (main_win) emit main_win->showMessage(msg, 0);
if (main_win) emit main_win->showMessage(msg, 2000);
}
MainWindow::MainWindow() : QMainWindow() {
@@ -43,7 +45,7 @@ MainWindow::MainWindow() : QMainWindow() {
qRegisterMetaType<ReplyMsgType>("ReplyMsgType");
installMessageHandler([this](ReplyMsgType type, const std::string msg) {
// use queued connection to recv the log messages from replay.
emit showMessage(QString::fromStdString(msg), 3000);
emit showMessage(QString::fromStdString(msg), 2000);
});
installDownloadProgressHandler([this](uint64_t cur, uint64_t total, bool success) {
emit updateProgressBar(cur, total, success);
@@ -125,6 +127,7 @@ void MainWindow::createActions() {
tools_menu->addAction(tr("Find &Similar Bits"), this, &MainWindow::findSimilarBits);
QMenu *help_menu = menuBar()->addMenu(tr("&Help"));
help_menu->addAction(tr("Help"), this, &MainWindow::onlineHelp)->setShortcuts(QKeySequence::HelpContents);
help_menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
}
@@ -173,6 +176,7 @@ void MainWindow::createStatusBar() {
progress_bar->setTextVisible(true);
progress_bar->setFixedSize({230, 16});
progress_bar->setVisible(false);
statusBar()->addWidget(new QLabel(tr("For Help,Press F1")));
statusBar()->addPermanentWidget(progress_bar);
}
@@ -422,3 +426,61 @@ void MainWindow::findSimilarBits() {
QObject::connect(dlg, &FindSimilarBitsDlg::openMessage, messages_widget, &MessagesWidget::selectMessage);
dlg->show();
}
void MainWindow::onlineHelp() {
if (auto help = findChild<HelpOverlay*>()) {
help->close();
} else {
help = new HelpOverlay(this);
help->setGeometry(rect());
help->show();
help->raise();
}
}
// HelpOverlay
HelpOverlay::HelpOverlay(MainWindow *parent) : QWidget(parent) {
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setAttribute(Qt::WA_DeleteOnClose);
parent->installEventFilter(this);
}
void HelpOverlay::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.fillRect(rect(), QColor(0, 0, 0, 50));
MainWindow *parent = (MainWindow *)parentWidget();
drawHelpForWidget(painter, parent->findChild<MessagesWidget *>());
drawHelpForWidget(painter, parent->findChild<BinaryView *>());
drawHelpForWidget(painter, parent->findChild<SignalView *>());
drawHelpForWidget(painter, parent->findChild<ChartsWidget *>());
drawHelpForWidget(painter, parent->findChild<VideoWidget *>());
}
void HelpOverlay::drawHelpForWidget(QPainter &painter, QWidget *w) {
if (w && w->isVisible() && !w->whatsThis().isEmpty()) {
QPoint pt = mapFromGlobal(w->mapToGlobal(w->rect().center()));
if (rect().contains(pt)) {
QTextDocument document;
document.setHtml(w->whatsThis());
QSize doc_size = document.size().toSize();
QPoint topleft = {pt.x() - doc_size.width() / 2, pt.y() - doc_size.height() / 2};
painter.translate(topleft);
painter.fillRect(QRect{{0, 0}, doc_size}, palette().toolTipBase());
document.drawContents(&painter);
painter.translate(-topleft);
}
}
}
bool HelpOverlay::eventFilter(QObject *obj, QEvent *event) {
if (obj == parentWidget() && event->type() == QEvent::Resize) {
QResizeEvent *resize_event = (QResizeEvent *)(event);
setGeometry(QRect{QPoint(0, 0), resize_event->size()});
}
return false;
}
void HelpOverlay::mouseReleaseEvent(QMouseEvent *event) {
close();
}
+14
View File
@@ -54,6 +54,7 @@ protected:
void setOption();
void findSimilarBits();
void undoStackCleanChanged(bool clean);
void onlineHelp();
VideoWidget *video_widget = nullptr;
QDockWidget *video_dock;
@@ -69,4 +70,17 @@ protected:
enum { MAX_RECENT_FILES = 15 };
QAction *recent_files_acts[MAX_RECENT_FILES] = {};
QMenu *open_recent_menu = nullptr;
friend class OnlineHelp;
};
class HelpOverlay : public QWidget {
Q_OBJECT
public:
HelpOverlay(MainWindow *parent);
protected:
void drawHelpForWidget(QPainter &painter, QWidget *w);
void paintEvent(QPaintEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
bool eventFilter(QObject *obj, QEvent *event) override;
};
+26 -21
View File
@@ -46,12 +46,12 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
QObject::connect(dbc(), &DBCManager::DBCFileChanged, model, &MessageListModel::sortMessages);
QObject::connect(dbc(), &DBCManager::msgUpdated, model, &MessageListModel::sortMessages);
QObject::connect(dbc(), &DBCManager::msgRemoved, model, &MessageListModel::sortMessages);
QObject::connect(model, &MessageListModel::modelReset, [this]() { selectMessage(current_msg_id); });
QObject::connect(model, &MessageListModel::modelReset, [this]() { selectMessage(*current_msg_id); });
QObject::connect(table_widget->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex &current, const QModelIndex &previous) {
if (current.isValid() && current.row() < model->msgs.size()) {
if (model->msgs[current.row()] != current_msg_id) {
if (model->msgs[current.row()] != *current_msg_id) {
current_msg_id = model->msgs[current.row()];
emit msgSelectionChanged(current_msg_id);
emit msgSelectionChanged(*current_msg_id);
}
}
});
@@ -65,9 +65,18 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
});
updateSuppressedButtons();
setWhatsThis(tr(R"(
<b>Message View</b><br/>
<!-- TODO: add descprition here -->
Byte color: <br />
<span style="color:gray;">■ </span> constant changing<br />
<span style="color:blue;">■ </span> increasing<br />
<span style="color:red;">■ </span> decreasing <br />
)"));
}
void MessagesWidget::selectMessage(const QString &msg_id) {
void MessagesWidget::selectMessage(const MessageId &msg_id) {
if (int row = model->msgs.indexOf(msg_id); row != -1) {
table_widget->selectionModel()->setCurrentIndex(model->index(row, 0), QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
}
@@ -86,7 +95,7 @@ void MessagesWidget::updateSuppressedButtons() {
void MessagesWidget::reset() {
model->reset();
filter->clear();
current_msg_id = "";
current_msg_id = std::nullopt;
updateSuppressedButtons();
}
@@ -106,31 +115,29 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0: return msgName(id);
case 1: return id;
case 1: return id.toString(); // TODO: put source and address in separate columns
case 2: return can_data.freq;
case 3: return can_data.count;
case 4: return toHex(can_data.dat);
}
} else if (role == Qt::UserRole && index.column() == 4) {
QList<QVariant> colors;
colors.reserve(can_data.dat.size());
for (int i = 0; i < can_data.dat.size(); i++){
if (suppressed_bytes.contains({id, i})) {
colors.append(QColor(255, 255, 255, 0));
} else {
colors.append(i < can_data.colors.size() ? can_data.colors[i] : QColor(255, 255, 255, 0));
QVector<QColor> colors = can_data.colors;
if (!suppressed_bytes.empty()) {
for (int i = 0; i < colors.size(); i++) {
if (suppressed_bytes.contains({id, i})) {
colors[i] = QColor(255, 255, 255, 0);
}
}
}
return colors;
return QVariant::fromValue(colors);
}
return {};
}
void MessageListModel::setFilterString(const QString &string) {
auto contains = [](const QString &id, const QString &txt) {
auto contains = [](const MessageId &id, const QString &txt) {
auto cs = Qt::CaseInsensitive;
if (id.contains(txt, cs) || msgName(id).contains(txt, cs)) return true;
if (id.toString().contains(txt, cs) || msgName(id).contains(txt, cs)) return true;
// Search by signal name
if (const auto msg = dbc()->msg(id)) {
for (auto &signal : msg->getSignals()) {
@@ -160,9 +167,7 @@ void MessageListModel::sortMessages() {
});
} else if (sort_column == 1) {
std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) {
auto ll = DBCManager::parseId(l);
auto rr = DBCManager::parseId(r);
return sort_order == Qt::AscendingOrder ? ll < rr : ll > rr;
return sort_order == Qt::AscendingOrder ? l < r : l > r;
});
} else if (sort_column == 2) {
std::sort(msgs.begin(), msgs.end(), [this](auto &l, auto &r) {
@@ -180,7 +185,7 @@ void MessageListModel::sortMessages() {
endResetModel();
}
void MessageListModel::msgsReceived(const QHash<QString, CanData> *new_msgs) {
void MessageListModel::msgsReceived(const QHash<MessageId, CanData> *new_msgs) {
int prev_row_count = msgs.size();
if (filter_str.isEmpty() && msgs.size() != can->can_msgs.size()) {
msgs = can->can_msgs.keys();
+8 -6
View File
@@ -1,5 +1,7 @@
#pragma once
#include <optional>
#include <QAbstractTableModel>
#include <QHeaderView>
#include <QLineEdit>
@@ -20,13 +22,13 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override { return msgs.size(); }
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
void setFilterString(const QString &string);
void msgsReceived(const QHash<QString, CanData> *new_msgs = nullptr);
void msgsReceived(const QHash<MessageId, CanData> *new_msgs = nullptr);
void sortMessages();
void suppress();
void clearSuppress();
void reset();
QStringList msgs;
QSet<std::pair<QString, int>> suppressed_bytes;
QList<MessageId> msgs;
QSet<std::pair<MessageId, int>> suppressed_bytes;
private:
QString filter_str;
@@ -39,18 +41,18 @@ class MessagesWidget : public QWidget {
public:
MessagesWidget(QWidget *parent);
void selectMessage(const QString &message_id);
void selectMessage(const MessageId &message_id);
QByteArray saveHeaderState() const { return table_widget->horizontalHeader()->saveState(); }
bool restoreHeaderState(const QByteArray &state) const { return table_widget->horizontalHeader()->restoreState(state); }
void updateSuppressedButtons();
void reset();
signals:
void msgSelectionChanged(const QString &message_id);
void msgSelectionChanged(const MessageId &message_id);
protected:
QTableView *table_widget;
QString current_msg_id;
std::optional<MessageId> current_msg_id;
QLineEdit *filter;
MessageListModel *model;
QPushButton *suppress_add;
+10 -5
View File
@@ -31,7 +31,7 @@ void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const Sign
}
}
void SignalModel::setMessage(const QString &id) {
void SignalModel::setMessage(const MessageId &id) {
msg_id = id;
filter_str = "";
refresh();
@@ -56,7 +56,7 @@ void SignalModel::refresh() {
endResetModel();
}
void SignalModel::updateState(const QHash<QString, CanData> *msgs) {
void SignalModel::updateState(const QHash<MessageId, CanData> *msgs) {
if (!msgs || (msgs->contains(msg_id))) {
auto &dat = can->lastMessage(msg_id).dat;
int row = 0;
@@ -230,13 +230,13 @@ void SignalModel::removeSignal(const Signal *sig) {
}
void SignalModel::handleMsgChanged(uint32_t address) {
if (address == DBCManager::parseId(msg_id).second) {
if (address == msg_id.address) {
refresh();
}
}
void SignalModel::handleSignalAdded(uint32_t address, const Signal *sig) {
if (address == DBCManager::parseId(msg_id).second) {
if (address == msg_id.address) {
int i = 0;
for (; i < root->children.size(); ++i) {
if (sig->start_bit < root->children[i]->sig->start_bit) break;
@@ -365,9 +365,14 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
QObject::connect(model, &QAbstractItemModel::rowsInserted, this, &SignalView::rowsChanged);
QObject::connect(model, &QAbstractItemModel::rowsRemoved, this, &SignalView::rowsChanged);
QObject::connect(dbc(), &DBCManager::signalAdded, [this](uint32_t address, const Signal *sig) { expandSignal(sig); });
setWhatsThis(tr(R"(
<b>Signal view</b><br />
<!-- TODO: add descprition here -->
)"));
}
void SignalView::setMessage(const QString &id) {
void SignalView::setMessage(const MessageId &id) {
msg_id = id;
filter_edit->clear();
model->setMessage(id);
+6 -6
View File
@@ -37,7 +37,7 @@ public:
QModelIndex parent(const QModelIndex &index) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
void setMessage(const QString &id);
void setMessage(const MessageId &id);
void setFilter(const QString &txt);
void addSignal(int start_bit, int size, bool little_endian);
bool saveSignal(const Signal *origin_s, Signal &s);
@@ -54,9 +54,9 @@ private:
void handleSignalRemoved(const Signal *sig);
void handleMsgChanged(uint32_t address);
void refresh();
void updateState(const QHash<QString, CanData> *msgs);
void updateState(const QHash<MessageId, CanData> *msgs);
QString msg_id;
MessageId msg_id;
QString filter_str;
std::unique_ptr<Item> root;
friend class SignalView;
@@ -76,7 +76,7 @@ class SignalView : public QWidget {
public:
SignalView(ChartsWidget *charts, QWidget *parent);
void setMessage(const QString &id);
void setMessage(const MessageId &id);
void signalHovered(const Signal *sig);
void updateChartState();
void expandSignal(const Signal *sig);
@@ -85,13 +85,13 @@ public:
signals:
void highlight(const Signal *sig);
void showChart(const QString &name, const Signal *sig, bool show, bool merge);
void showChart(const MessageId &id, const Signal *sig, bool show, bool merge);
private:
void rowsChanged();
void leaveEvent(QEvent *event);
QString msg_id;
MessageId msg_id;
QTreeView *tree;
QLineEdit *filter_edit;
ChartsWidget *charts;
+12 -12
View File
@@ -4,12 +4,12 @@ AbstractStream *can = nullptr;
AbstractStream::AbstractStream(QObject *parent, bool is_live_streaming) : is_live_streaming(is_live_streaming), QObject(parent) {
can = this;
new_msgs = std::make_unique<QHash<QString, CanData>>();
new_msgs = std::make_unique<QHash<MessageId, CanData>>();
QObject::connect(this, &AbstractStream::received, this, &AbstractStream::process, Qt::QueuedConnection);
QObject::connect(this, &AbstractStream::seekedTo, this, &AbstractStream::updateLastMsgsTo);
}
void AbstractStream::process(QHash<QString, CanData> *messages) {
void AbstractStream::process(QHash<MessageId, CanData> *messages) {
for (auto it = messages->begin(); it != messages->end(); ++it) {
can_msgs[it.key()] = it.value();
}
@@ -25,7 +25,7 @@ bool AbstractStream::updateEvent(const Event *event) {
if (event->which == cereal::Event::Which::CAN) {
double current_sec = event->mono_time / 1e9 - routeStartTime();
for (const auto &c : event->event.getCan()) {
QString id = QString("%1:%2").arg(c.getSrc()).arg(c.getAddress(), 1, 16);
MessageId id = {.source = c.getSrc(), .address = c.getAddress()};
CanData &data = (*new_msgs)[id];
data.ts = current_sec;
data.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size());
@@ -44,29 +44,30 @@ bool AbstractStream::updateEvent(const Event *event) {
prev_update_ts = ts;
// use pointer to avoid data copy in queued connection.
emit received(new_msgs.release());
new_msgs.reset(new QHash<QString, CanData>);
new_msgs.reset(new QHash<MessageId, CanData>);
new_msgs->reserve(100);
}
}
return true;
}
const CanData &AbstractStream::lastMessage(const QString &id) {
const CanData &AbstractStream::lastMessage(const MessageId &id) {
static CanData empty_data;
auto it = can_msgs.find(id);
return it != can_msgs.end() ? it.value() : empty_data;
}
void AbstractStream::updateLastMsgsTo(double sec) {
QHash<std::pair<uint8_t, uint32_t>, CanData> last_msgs; // Much faster than QHash<String, CanData>
QHash<MessageId, CanData> last_msgs;
last_msgs.reserve(can_msgs.size());
double route_start_time = routeStartTime();
uint64_t last_ts = (sec + route_start_time) * 1e9;
auto last = std::upper_bound(events()->rbegin(), events()->rend(), last_ts, [](uint64_t ts, auto &e) { return e->mono_time < ts; });
for (auto it = last; it != events()->rend(); ++it) {
auto evs = events();
auto last = std::upper_bound(evs->rbegin(), evs->rend(), last_ts, [](uint64_t ts, auto &e) { return e->mono_time < ts; });
for (auto it = last; it != evs->rend(); ++it) {
if ((*it)->which == cereal::Event::Which::CAN) {
for (const auto &c : (*it)->event.getCan()) {
auto &m = last_msgs[{c.getSrc(), c.getAddress()}];
auto &m = last_msgs[{.source = c.getSrc(), .address = c.getAddress()}];
if (++m.count == 1) {
m.ts = ((*it)->mono_time / 1e9) - route_start_time;
m.dat = QByteArray((char *)c.getDat().begin(), c.getDat().size());
@@ -87,9 +88,8 @@ void AbstractStream::updateLastMsgsTo(double sec) {
counters.clear();
can_msgs.clear();
for (auto it = last_msgs.cbegin(); it != last_msgs.cend(); ++it) {
QString msg_id = QString("%1:%2").arg(it.key().first).arg(it.key().second, 1, 16);
can_msgs[msg_id] = it.value();
counters[msg_id] = it.value().count;
can_msgs[it.key()] = it.value();
counters[it.key()] = it.value().count;
}
emit updated();
emit msgsReceived(&can_msgs);
+9 -8
View File
@@ -5,6 +5,7 @@
#include <QColor>
#include <QHash>
#include "tools/cabana/dbcmanager.h"
#include "tools/cabana/settings.h"
#include "tools/cabana/util.h"
#include "tools/replay/replay.h"
@@ -33,7 +34,7 @@ public:
virtual double routeStartTime() const { return 0; }
virtual double currentSec() const = 0;
virtual QDateTime currentDateTime() const { return {}; }
virtual const CanData &lastMessage(const QString &id);
virtual const CanData &lastMessage(const MessageId &id);
virtual VisionStreamType visionStreamType() const { return VISION_STREAM_ROAD; }
virtual const Route *route() const { return nullptr; }
virtual const std::vector<Event *> *events() const = 0;
@@ -49,22 +50,22 @@ signals:
void streamStarted();
void eventsMerged();
void updated();
void msgsReceived(const QHash<QString, CanData> *);
void received(QHash<QString, CanData> *);
void msgsReceived(const QHash<MessageId, CanData> *);
void received(QHash<MessageId, CanData> *);
public:
QHash<QString, CanData> can_msgs;
QHash<MessageId, CanData> can_msgs;
protected:
void process(QHash<QString, CanData> *);
void process(QHash<MessageId, CanData> *);
bool updateEvent(const Event *event);
void updateLastMsgsTo(double sec);
bool is_live_streaming = false;
std::atomic<bool> processing = false;
QHash<QString, uint32_t> counters;
std::unique_ptr<QHash<QString, CanData>> new_msgs;
QHash<QString, ChangeTracker> change_trackers;
QHash<MessageId, uint32_t> counters;
std::unique_ptr<QHash<MessageId, CanData>> new_msgs;
QHash<MessageId, ChangeTracker> change_trackers;
};
// A global pointer referring to the unique AbstractStream object
+5 -3
View File
@@ -20,10 +20,10 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::Wi
bus_combo = new QComboBox(this);
QSet<uint8_t> bus_set;
for (auto it = can->can_msgs.begin(); it != can->can_msgs.end(); ++it) {
bus_set << DBCManager::parseId(it.key()).first;
bus_set << it.key().source;
}
for (uint8_t bus : bus_set) {
bus_combo->addItem(QString::number(bus));
bus_combo->addItem(QString::number(bus), bus);
}
bus_combo->model()->sort(0);
bus_combo->setCurrentIndex(0);
@@ -69,9 +69,11 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::Wi
setMinimumSize({700, 500});
QObject::connect(search_btn, &QPushButton::clicked, this, &FindSimilarBitsDlg::find);
QObject::connect(table, &QTableWidget::doubleClicked, [this](const QModelIndex &index) {
if (index.isValid()) {
emit openMessage(bus_combo->currentText() + ":" + table->item(index.row(), 0)->text());
MessageId msg_id = {.source = (uint8_t)bus_combo->currentData().toUInt(), .address = table->item(index.row(), 0)->text().toUInt(0, 16)};
emit openMessage(msg_id);
}
});
}
+3 -1
View File
@@ -6,6 +6,8 @@
#include <QSpinBox>
#include <QTableWidget>
#include "tools/cabana/dbcmanager.h"
class FindSimilarBitsDlg : public QDialog {
Q_OBJECT
@@ -13,7 +15,7 @@ public:
FindSimilarBitsDlg(QWidget *parent);
signals:
void openMessage(const QString &msg_id);
void openMessage(const MessageId &msg_id);
private:
struct mismatched_struct {
+6 -19
View File
@@ -66,13 +66,6 @@ void ChangeTracker::clear() {
colors.clear();
}
QList<QVariant> ChangeTracker::toVariantList(const QVector<QColor> &colors) {
QList<QVariant> ret;
ret.reserve(colors.size());
for (auto &c : colors) ret.append(c);
return ret;
}
// MessageBytesDelegate
MessageBytesDelegate::MessageBytesDelegate(QObject *parent) : QStyledItemDelegate(parent) {
@@ -89,12 +82,8 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
return;
}
if ((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Active)) {
painter->setPen(option.palette.color(QPalette::HighlightedText));
} else {
painter->setPen(option.palette.color(QPalette::Text));
}
auto color_role = option.state & QStyle::State_Selected ? QPalette::HighlightedText: QPalette::Text;
painter->setPen(option.palette.color(color_role));
painter->setFont(fixed_font);
QRect space = painter->boundingRect(opt.rect, opt.displayAlignment, " ");
QRect pos = painter->boundingRect(opt.rect, opt.displayAlignment, "00");
@@ -103,15 +92,13 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
int m = space.width() / 2;
const QMargins margins(m, m, m, m);
QList<QVariant> colors = index.data(Qt::UserRole).toList();
int i = 0;
for (auto &byte : byte_list) {
auto colors = index.data(Qt::UserRole).value<QVector<QColor>>();
for (int i = 0; i < byte_list.size(); ++i) {
if (i < colors.size()) {
painter->fillRect(pos.marginsAdded(margins), colors[i].value<QColor>());
painter->fillRect(pos.marginsAdded(margins), colors[i]);
}
painter->drawText(pos, opt.displayAlignment, byte);
painter->drawText(pos, opt.displayAlignment, byte_list[i]);
pos.moveLeft(pos.right() + space.width());
i++;
}
}
-1
View File
@@ -14,7 +14,6 @@
class ChangeTracker {
public:
void compute(const QByteArray &dat, double ts, uint32_t freq);
static QList<QVariant> toVariantList(const QVector<QColor> &colors);
void clear();
QVector<double> last_change_t;
+7
View File
@@ -53,6 +53,13 @@ VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) {
QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState);
QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState);
updatePlayBtnState();
setWhatsThis(tr(R"(
<b>Video</b><br />
<!-- TODO: add descprition here -->
Shortcuts:<br />
Pause/Resume: <span style="background-color:lightGray;color:gray"> space </span></br>
)"));
}
QWidget *VideoWidget::createCameraWidget() {
+3
View File
@@ -211,7 +211,10 @@ def fake_driver_monitoring(exit_event: threading.Event):
while not exit_event.is_set():
# dmonitoringmodeld output
dat = messaging.new_message('driverStateV2')
dat.driverStateV2.leftDriverData.faceOrientation = [0., 0., 0.]
dat.driverStateV2.leftDriverData.faceProb = 1.0
dat.driverStateV2.rightDriverData.faceOrientation = [0., 0., 0.]
dat.driverStateV2.rightDriverData.faceProb = 1.0
pm.send('driverStateV2', dat)
# dmonitoringd output