Merge remote-tracking branch 'upstream/master' into big-test-models

pull/29291/head
Shane Smiskol 3 years ago
commit 9fc84f5844
  1. 2
      opendbc
  2. 2
      panda
  3. 3
      selfdrive/boardd/boardd.cc
  4. 3
      selfdrive/boardd/panda.cc
  5. 4
      selfdrive/car/gm/carstate.py
  6. 2
      selfdrive/controls/controlsd.py
  7. 3
      selfdrive/controls/lib/events.py
  8. 2
      selfdrive/debug/test_fw_query_on_routes.py
  9. 2
      selfdrive/modeld/models/supercombo.onnx
  10. 2
      selfdrive/test/process_replay/model_replay_ref_commit
  11. 2
      selfdrive/test/process_replay/ref_commit
  12. 1
      selfdrive/test/setup_device_ci.sh
  13. 9
      selfdrive/thermald/power_monitoring.py
  14. 63
      selfdrive/thermald/tests/test_power_monitoring.py
  15. 3
      selfdrive/thermald/thermald.py
  16. 29
      tools/cabana/binaryview.cc
  17. 4
      tools/cabana/binaryview.h
  18. 2
      tools/cabana/cabana.cc
  19. 4
      tools/cabana/chartswidget.cc
  20. 1
      tools/cabana/chartswidget.h
  21. 3
      tools/cabana/detailwidget.cc
  22. 17
      tools/cabana/historylog.cc
  23. 1
      tools/cabana/historylog.h
  24. 8
      tools/cabana/settings.cc
  25. 2
      tools/cabana/settings.h
  26. 2
      tools/cabana/signaledit.cc

@ -1 +1 @@
Subproject commit 296f190000a2e71408e207ba21a2257cc853ec15 Subproject commit 871e054d9a94629d92c22fe89cae71af5b0d3823

@ -1 +1 @@
Subproject commit 2e90b6f308dc09bf1734f6cb5cc990cb8149486d Subproject commit c075050d5df70570cfadd8c0d7507f25fe67d247

@ -528,9 +528,6 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) {
cnt++; cnt++;
sm.update(1000); // TODO: what happens if EINTR is sent while in sm.update? sm.update(1000); // TODO: what happens if EINTR is sent while in sm.update?
// Other pandas don't have fan/IR to control
if (panda->hw_type != cereal::PandaState::PandaType::UNO && panda->hw_type != cereal::PandaState::PandaType::DOS) continue;
if (sm.updated("deviceState") && !no_fan_control) { if (sm.updated("deviceState") && !no_fan_control) {
// Fan speed // Fan speed
uint16_t fan_speed = sm["deviceState"].getDeviceState().getFanSpeedPercentDesired(); uint16_t fan_speed = sm["deviceState"].getDeviceState().getFanSpeedPercentDesired();

@ -24,7 +24,8 @@ Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) {
(hw_type != cereal::PandaState::PandaType::GREY_PANDA)); (hw_type != cereal::PandaState::PandaType::GREY_PANDA));
has_rtc = (hw_type == cereal::PandaState::PandaType::UNO) || has_rtc = (hw_type == cereal::PandaState::PandaType::UNO) ||
(hw_type == cereal::PandaState::PandaType::DOS); (hw_type == cereal::PandaState::PandaType::DOS) ||
(hw_type == cereal::PandaState::PandaType::TRES);
return; return;
} }

@ -100,6 +100,9 @@ class CarState(CarStateBase):
if self.CP.networkLocation == NetworkLocation.fwdCamera: if self.CP.networkLocation == NetworkLocation.fwdCamera:
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0 ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
# openpilot controls nonAdaptive when not pcmCruise
if self.CP.pcmCruise:
ret.cruiseState.nonAdaptive = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] not in (2, 3)
return ret return ret
@ -112,6 +115,7 @@ class CarState(CarStateBase):
("AEBCmdActive", "AEBCmd"), ("AEBCmdActive", "AEBCmd"),
("RollingCounter", "ASCMLKASteeringCmd"), ("RollingCounter", "ASCMLKASteeringCmd"),
("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"), ("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"),
("ACCCruiseState", "ASCMActiveCruiseControlStatus"),
] ]
checks += [ checks += [
("AEBCmd", 10), ("AEBCmd", 10),

@ -275,7 +275,7 @@ class Controls:
# self.events.add(EventName.highCpuUsage) # self.events.add(EventName.highCpuUsage)
# Alert if fan isn't spinning for 5 seconds # Alert if fan isn't spinning for 5 seconds
if self.sm['peripheralState'].pandaType == PandaType.dos: if self.sm['peripheralState'].pandaType != log.PandaState.PandaType.unknown:
if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50: if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50:
if (self.sm.frame - self.last_functional_fan_frame) * DT_CTRL > 5.0: if (self.sm.frame - self.last_functional_fan_frame) * DT_CTRL > 5.0:
self.events.add(EventName.fanMalfunction) self.events.add(EventName.fanMalfunction)

@ -250,10 +250,9 @@ def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messag
def no_gps_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: def no_gps_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
gps_integrated = sm['peripheralState'].pandaType in (log.PandaState.PandaType.uno, log.PandaState.PandaType.dos)
return Alert( return Alert(
"Poor GPS reception", "Poor GPS reception",
"Hardware malfunctioning if sky is visible" if gps_integrated else "Check GPS antenna placement", "Hardware malfunctioning if sky is visible",
AlertStatus.normal, AlertSize.mid, AlertStatus.normal, AlertSize.mid,
Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=300.) Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=300.)

@ -68,7 +68,7 @@ if __name__ == "__main__":
CP = None CP = None
for msg in lr: for msg in lr:
if msg.which() == "pandaStates": if msg.which() == "pandaStates":
if msg.pandaStates[0].pandaType not in ('uno', 'blackPanda', 'dos'): if msg.pandaStates[0].pandaType in ('unknown', 'whitePanda', 'greyPanda', 'pedal'):
print("wrong panda type") print("wrong panda type")
break break

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c73998c9f428380dd2282b451de6011469b717498ae83578cbf7aa95948910f7 oid sha256:db746e3753de84367595fedd089c2bd41b06bd401ea28e085663533d0e63d74b
size 45962473 size 45962473

@ -1 +1 @@
ca02aa240e629920ad88a6510213cb0a153af92b 30efb4238327d723e17a3bda7e7c19c18f8a3b18

@ -1 +1 @@
959e63af52d9efdb62156cab4b773f88b154fd75 ff508a46616a1a3d66e8d1154d123ffd11025003

@ -29,6 +29,7 @@ sudo abctl --set_success
# patch sshd config # patch sshd config
sudo mount -o rw,remount / sudo mount -o rw,remount /
echo tici-$(cat /proc/cmdline | sed -e 's/^.*androidboot.serialno=//' -e 's/ .*$//') | sudo tee /etc/hostname
sudo sed -i "s,/data/params/d/GithubSshKeys,/usr/comma/setup_keys," /etc/ssh/sshd_config sudo sed -i "s,/data/params/d/GithubSshKeys,/usr/comma/setup_keys," /etc/ssh/sshd_config
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl restart ssh sudo systemctl restart ssh

@ -1,7 +1,6 @@
import threading import threading
from typing import Optional from typing import Optional
from cereal import log
from common.params import Params, put_nonblocking from common.params import Params, put_nonblocking
from common.realtime import sec_since_boot from common.realtime import sec_since_boot
from system.hardware import HARDWARE from system.hardware import HARDWARE
@ -39,12 +38,12 @@ class PowerMonitoring:
self.car_battery_capacity_uWh = max((CAR_BATTERY_CAPACITY_uWh / 10), int(car_battery_capacity_uWh)) self.car_battery_capacity_uWh = max((CAR_BATTERY_CAPACITY_uWh / 10), int(car_battery_capacity_uWh))
# Calculation tick # Calculation tick
def calculate(self, peripheralState, ignition): def calculate(self, voltage: Optional[int], ignition: bool):
try: try:
now = sec_since_boot() now = sec_since_boot()
# If peripheralState is None, we're probably not in a car, so we don't care # If peripheralState is None, we're probably not in a car, so we don't care
if peripheralState is None or peripheralState.pandaType == log.PandaState.PandaType.unknown: if voltage is None:
with self.integration_lock: with self.integration_lock:
self.last_measurement_time = None self.last_measurement_time = None
self.next_pulsed_measurement_time = None self.next_pulsed_measurement_time = None
@ -52,8 +51,8 @@ class PowerMonitoring:
return return
# Low-pass battery voltage # Low-pass battery voltage
self.car_voltage_instant_mV = peripheralState.voltage self.car_voltage_instant_mV = voltage
self.car_voltage_mV = ((peripheralState.voltage * CAR_VOLTAGE_LOW_PASS_K) + (self.car_voltage_mV * (1 - CAR_VOLTAGE_LOW_PASS_K))) self.car_voltage_mV = ((voltage * CAR_VOLTAGE_LOW_PASS_K) + (self.car_voltage_mV * (1 - CAR_VOLTAGE_LOW_PASS_K)))
statlog.gauge("car_voltage", self.car_voltage_mV / 1e3) statlog.gauge("car_voltage", self.car_voltage_mV / 1e3)
# Cap the car battery power and save it in a param every 10-ish seconds # Cap the car battery power and save it in a param every 10-ish seconds

@ -1,10 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from parameterized import parameterized
from cereal import log
import cereal.messaging as messaging
from common.params import Params from common.params import Params
params = Params() params = Params()
@ -21,7 +18,8 @@ with patch("common.realtime.sec_since_boot", new=mock_sec_since_boot):
CAR_CHARGING_RATE_W, VBATT_PAUSE_CHARGING CAR_CHARGING_RATE_W, VBATT_PAUSE_CHARGING
TEST_DURATION_S = 50 TEST_DURATION_S = 50
ALL_PANDA_TYPES = [(log.PandaState.PandaType.dos,)] GOOD_VOLTAGE = 12 * 1e3
VOLTAGE_BELOW_PAUSE_CHARGING = (VBATT_PAUSE_CHARGING - 1) * 1e3
def pm_patch(name, value, constant=False): def pm_patch(name, value, constant=False):
if constant: if constant:
@ -34,12 +32,6 @@ class TestPowerMonitoring(unittest.TestCase):
params.remove("CarBatteryCapacity") params.remove("CarBatteryCapacity")
params.remove("DisablePowerDown") params.remove("DisablePowerDown")
def mock_peripheralState(self, hw_type, car_voltage=12):
ps = messaging.new_message('peripheralState').peripheralState
ps.pandaType = hw_type
ps.voltage = car_voltage * 1e3
return ps
# Test to see that it doesn't do anything when pandaState is None # Test to see that it doesn't do anything when pandaState is None
def test_pandaState_present(self): def test_pandaState_present(self):
pm = PowerMonitoring() pm = PowerMonitoring()
@ -49,75 +41,68 @@ class TestPowerMonitoring(unittest.TestCase):
self.assertEqual(pm.get_car_battery_capacity(), (CAR_BATTERY_CAPACITY_uWh / 10)) self.assertEqual(pm.get_car_battery_capacity(), (CAR_BATTERY_CAPACITY_uWh / 10))
# Test to see that it doesn't integrate offroad when ignition is True # Test to see that it doesn't integrate offroad when ignition is True
@parameterized.expand(ALL_PANDA_TYPES) def test_offroad_ignition(self):
def test_offroad_ignition(self, hw_type):
pm = PowerMonitoring() pm = PowerMonitoring()
for _ in range(10): for _ in range(10):
pm.calculate(self.mock_peripheralState(hw_type), True) pm.calculate(GOOD_VOLTAGE, True)
self.assertEqual(pm.get_power_used(), 0) self.assertEqual(pm.get_power_used(), 0)
# Test to see that it integrates with discharging battery # Test to see that it integrates with discharging battery
@parameterized.expand(ALL_PANDA_TYPES) def test_offroad_integration_discharging(self):
def test_offroad_integration_discharging(self, hw_type):
POWER_DRAW = 4 POWER_DRAW = 4
with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW):
pm = PowerMonitoring() pm = PowerMonitoring()
for _ in range(TEST_DURATION_S + 1): for _ in range(TEST_DURATION_S + 1):
pm.calculate(self.mock_peripheralState(hw_type), False) pm.calculate(GOOD_VOLTAGE, False)
expected_power_usage = ((TEST_DURATION_S/3600) * POWER_DRAW * 1e6) expected_power_usage = ((TEST_DURATION_S/3600) * POWER_DRAW * 1e6)
self.assertLess(abs(pm.get_power_used() - expected_power_usage), 10) self.assertLess(abs(pm.get_power_used() - expected_power_usage), 10)
# Test to check positive integration of car_battery_capacity # Test to check positive integration of car_battery_capacity
@parameterized.expand(ALL_PANDA_TYPES) def test_car_battery_integration_onroad(self):
def test_car_battery_integration_onroad(self, hw_type):
POWER_DRAW = 4 POWER_DRAW = 4
with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW):
pm = PowerMonitoring() pm = PowerMonitoring()
pm.car_battery_capacity_uWh = 0 pm.car_battery_capacity_uWh = 0
for _ in range(TEST_DURATION_S + 1): for _ in range(TEST_DURATION_S + 1):
pm.calculate(self.mock_peripheralState(hw_type), True) pm.calculate(GOOD_VOLTAGE, True)
expected_capacity = ((TEST_DURATION_S/3600) * CAR_CHARGING_RATE_W * 1e6) expected_capacity = ((TEST_DURATION_S/3600) * CAR_CHARGING_RATE_W * 1e6)
self.assertLess(abs(pm.get_car_battery_capacity() - expected_capacity), 10) self.assertLess(abs(pm.get_car_battery_capacity() - expected_capacity), 10)
# Test to check positive integration upper limit # Test to check positive integration upper limit
@parameterized.expand(ALL_PANDA_TYPES) def test_car_battery_integration_upper_limit(self):
def test_car_battery_integration_upper_limit(self, hw_type):
POWER_DRAW = 4 POWER_DRAW = 4
with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW):
pm = PowerMonitoring() pm = PowerMonitoring()
pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh - 1000 pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh - 1000
for _ in range(TEST_DURATION_S + 1): for _ in range(TEST_DURATION_S + 1):
pm.calculate(self.mock_peripheralState(hw_type), True) pm.calculate(GOOD_VOLTAGE, True)
estimated_capacity = CAR_BATTERY_CAPACITY_uWh + (CAR_CHARGING_RATE_W / 3600 * 1e6) estimated_capacity = CAR_BATTERY_CAPACITY_uWh + (CAR_CHARGING_RATE_W / 3600 * 1e6)
self.assertLess(abs(pm.get_car_battery_capacity() - estimated_capacity), 10) self.assertLess(abs(pm.get_car_battery_capacity() - estimated_capacity), 10)
# Test to check negative integration of car_battery_capacity # Test to check negative integration of car_battery_capacity
@parameterized.expand(ALL_PANDA_TYPES) def test_car_battery_integration_offroad(self):
def test_car_battery_integration_offroad(self, hw_type):
POWER_DRAW = 4 POWER_DRAW = 4
with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW):
pm = PowerMonitoring() pm = PowerMonitoring()
pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
for _ in range(TEST_DURATION_S + 1): for _ in range(TEST_DURATION_S + 1):
pm.calculate(self.mock_peripheralState(hw_type), False) pm.calculate(GOOD_VOLTAGE, False)
expected_capacity = CAR_BATTERY_CAPACITY_uWh - ((TEST_DURATION_S/3600) * POWER_DRAW * 1e6) expected_capacity = CAR_BATTERY_CAPACITY_uWh - ((TEST_DURATION_S/3600) * POWER_DRAW * 1e6)
self.assertLess(abs(pm.get_car_battery_capacity() - expected_capacity), 10) self.assertLess(abs(pm.get_car_battery_capacity() - expected_capacity), 10)
# Test to check negative integration lower limit # Test to check negative integration lower limit
@parameterized.expand(ALL_PANDA_TYPES) def test_car_battery_integration_lower_limit(self):
def test_car_battery_integration_lower_limit(self, hw_type):
POWER_DRAW = 4 POWER_DRAW = 4
with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW):
pm = PowerMonitoring() pm = PowerMonitoring()
pm.car_battery_capacity_uWh = 1000 pm.car_battery_capacity_uWh = 1000
for _ in range(TEST_DURATION_S + 1): for _ in range(TEST_DURATION_S + 1):
pm.calculate(self.mock_peripheralState(hw_type), False) pm.calculate(GOOD_VOLTAGE, False)
estimated_capacity = 0 - ((1/3600) * POWER_DRAW * 1e6) estimated_capacity = 0 - ((1/3600) * POWER_DRAW * 1e6)
self.assertLess(abs(pm.get_car_battery_capacity() - estimated_capacity), 10) self.assertLess(abs(pm.get_car_battery_capacity() - estimated_capacity), 10)
# Test to check policy of stopping charging after MAX_TIME_OFFROAD_S # Test to check policy of stopping charging after MAX_TIME_OFFROAD_S
@parameterized.expand(ALL_PANDA_TYPES) def test_max_time_offroad(self):
def test_max_time_offroad(self, hw_type):
MOCKED_MAX_OFFROAD_TIME = 3600 MOCKED_MAX_OFFROAD_TIME = 3600
POWER_DRAW = 0 # To stop shutting down for other reasons POWER_DRAW = 0 # To stop shutting down for other reasons
with pm_patch("MAX_TIME_OFFROAD_S", MOCKED_MAX_OFFROAD_TIME, constant=True), pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): with pm_patch("MAX_TIME_OFFROAD_S", MOCKED_MAX_OFFROAD_TIME, constant=True), pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW):
@ -125,25 +110,22 @@ class TestPowerMonitoring(unittest.TestCase):
pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
start_time = ssb start_time = ssb
ignition = False ignition = False
peripheralState = self.mock_peripheralState(hw_type)
while ssb <= start_time + MOCKED_MAX_OFFROAD_TIME: while ssb <= start_time + MOCKED_MAX_OFFROAD_TIME:
pm.calculate(peripheralState, ignition) pm.calculate(GOOD_VOLTAGE, ignition)
if (ssb - start_time) % 1000 == 0 and ssb < start_time + MOCKED_MAX_OFFROAD_TIME: if (ssb - start_time) % 1000 == 0 and ssb < start_time + MOCKED_MAX_OFFROAD_TIME:
self.assertFalse(pm.should_shutdown(ignition, True, start_time, False)) self.assertFalse(pm.should_shutdown(ignition, True, start_time, False))
self.assertTrue(pm.should_shutdown(ignition, True, start_time, False)) self.assertTrue(pm.should_shutdown(ignition, True, start_time, False))
# Test to check policy of stopping charging when the car voltage is too low # Test to check policy of stopping charging when the car voltage is too low
@parameterized.expand(ALL_PANDA_TYPES) def test_car_voltage(self):
def test_car_voltage(self, hw_type):
POWER_DRAW = 0 # To stop shutting down for other reasons POWER_DRAW = 0 # To stop shutting down for other reasons
TEST_TIME = 100 TEST_TIME = 100
with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW): with pm_patch("HARDWARE.get_current_power_draw", POWER_DRAW):
pm = PowerMonitoring() pm = PowerMonitoring()
pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
ignition = False ignition = False
peripheralState = self.mock_peripheralState(hw_type, car_voltage=(VBATT_PAUSE_CHARGING - 1))
for i in range(TEST_TIME): for i in range(TEST_TIME):
pm.calculate(peripheralState, ignition) pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
if i % 10 == 0: if i % 10 == 0:
self.assertEqual(pm.should_shutdown(ignition, True, ssb, True), (pm.car_voltage_mV < VBATT_PAUSE_CHARGING*1e3)) self.assertEqual(pm.should_shutdown(ignition, True, ssb, True), (pm.car_voltage_mV < VBATT_PAUSE_CHARGING*1e3))
self.assertTrue(pm.should_shutdown(ignition, True, ssb, True)) self.assertTrue(pm.should_shutdown(ignition, True, ssb, True))
@ -157,9 +139,8 @@ class TestPowerMonitoring(unittest.TestCase):
pm = PowerMonitoring() pm = PowerMonitoring()
pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
ignition = False ignition = False
peripheralState = self.mock_peripheralState(log.PandaState.PandaType.uno, car_voltage=(VBATT_PAUSE_CHARGING - 1))
for i in range(TEST_TIME): for i in range(TEST_TIME):
pm.calculate(peripheralState, ignition) pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
if i % 10 == 0: if i % 10 == 0:
self.assertFalse(pm.should_shutdown(ignition, True, ssb, False)) self.assertFalse(pm.should_shutdown(ignition, True, ssb, False))
self.assertFalse(pm.should_shutdown(ignition, True, ssb, False)) self.assertFalse(pm.should_shutdown(ignition, True, ssb, False))
@ -172,9 +153,8 @@ class TestPowerMonitoring(unittest.TestCase):
pm = PowerMonitoring() pm = PowerMonitoring()
pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
ignition = True ignition = True
peripheralState = self.mock_peripheralState(log.PandaState.PandaType.uno, car_voltage=(VBATT_PAUSE_CHARGING - 1))
for i in range(TEST_TIME): for i in range(TEST_TIME):
pm.calculate(peripheralState, ignition) pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
if i % 10 == 0: if i % 10 == 0:
self.assertFalse(pm.should_shutdown(ignition, True, ssb, False)) self.assertFalse(pm.should_shutdown(ignition, True, ssb, False))
self.assertFalse(pm.should_shutdown(ignition, True, ssb, False)) self.assertFalse(pm.should_shutdown(ignition, True, ssb, False))
@ -188,9 +168,8 @@ class TestPowerMonitoring(unittest.TestCase):
pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
ignition = False ignition = False
peripheralState = self.mock_peripheralState(log.PandaState.PandaType.uno, car_voltage=(VBATT_PAUSE_CHARGING - 1))
for i in range(TEST_TIME): for i in range(TEST_TIME):
pm.calculate(peripheralState, ignition) pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
if i % 10 == 0: if i % 10 == 0:
self.assertFalse(pm.should_shutdown(ignition, False, ssb, False)) self.assertFalse(pm.should_shutdown(ignition, False, ssb, False))
self.assertFalse(pm.should_shutdown(ignition, False, ssb, False)) self.assertFalse(pm.should_shutdown(ignition, False, ssb, False))

@ -347,7 +347,8 @@ def thermald_thread(end_event, hw_queue):
off_ts = sec_since_boot() off_ts = sec_since_boot()
# Offroad power monitoring # Offroad power monitoring
power_monitor.calculate(peripheralState, onroad_conditions["ignition"]) voltage = None if peripheralState.pandaType == log.PandaState.PandaType.unknown else peripheralState.voltage
power_monitor.calculate(voltage, onroad_conditions["ignition"])
msg.deviceState.offroadPowerUsageUwh = power_monitor.get_power_used() msg.deviceState.offroadPowerUsageUwh = power_monitor.get_power_used()
msg.deviceState.carBatteryCapacityUwh = max(0, power_monitor.get_car_battery_capacity()) msg.deviceState.carBatteryCapacityUwh = max(0, power_monitor.get_car_battery_capacity())
current_power_draw = HARDWARE.get_current_power_draw() current_power_draw = HARDWARE.get_current_power_draw()

@ -1,6 +1,5 @@
#include "tools/cabana/binaryview.h" #include "tools/cabana/binaryview.h"
#include <QApplication>
#include <QFontDatabase> #include <QFontDatabase>
#include <QHeaderView> #include <QHeaderView>
#include <QMouseEvent> #include <QMouseEvent>
@ -74,14 +73,18 @@ void BinaryView::mousePressEvent(QMouseEvent *event) {
event->accept(); event->accept();
} }
void BinaryView::mouseMoveEvent(QMouseEvent *event) { void BinaryView::highlightPosition(const QPoint &pos) {
if (auto index = indexAt(event->pos()); index.isValid()) { if (auto index = indexAt(viewport()->mapFromGlobal(pos)); index.isValid()) {
auto item = (BinaryViewModel::Item *)index.internalPointer(); auto item = (BinaryViewModel::Item *)index.internalPointer();
const Signal *sig = item->sigs.isEmpty() ? nullptr : item->sigs.back(); const Signal *sig = item->sigs.isEmpty() ? nullptr : item->sigs.back();
highlight(sig); highlight(sig);
sig ? QToolTip::showText(event->globalPos(), sig->name.c_str(), this, rect()) sig ? QToolTip::showText(pos, sig->name.c_str(), this, rect())
: QToolTip::hideText(); : QToolTip::hideText();
} }
}
void BinaryView::mouseMoveEvent(QMouseEvent *event) {
highlightPosition(event->globalPos());
QTableView::mouseMoveEvent(event); QTableView::mouseMoveEvent(event);
} }
@ -116,6 +119,7 @@ void BinaryView::setMessage(const QString &message_id) {
anchor_index = QModelIndex(); anchor_index = QModelIndex();
resize_sig = nullptr; resize_sig = nullptr;
hovered_sig = nullptr; hovered_sig = nullptr;
highlightPosition(QCursor::pos());
updateState(); updateState();
} }
@ -232,19 +236,18 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
BinaryView *bin_view = (BinaryView *)parent(); BinaryView *bin_view = (BinaryView *)parent();
painter->save(); painter->save();
// background if (index.column() == 8) {
if (option.state & QStyle::State_Selected) { painter->setFont(hex_font);
} else if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, selection_color); painter->fillRect(option.rect, selection_color);
} else if (!bin_view->selectionModel()->hasSelection() || !item->sigs.contains(bin_view->resize_sig)) { painter->setPen(QApplication::style()->standardPalette().color(QPalette::BrightText));
} else if (!item->sigs.isEmpty() && (!bin_view->selectionModel()->hasSelection() || !item->sigs.contains(bin_view->resize_sig))) {
painter->fillRect(option.rect, item->bg_color); painter->fillRect(option.rect, item->bg_color);
painter->setPen(item->sigs.contains(bin_view->hovered_sig)
? QApplication::style()->standardPalette().color(QPalette::BrightText)
: Qt::black);
} }
// text
if (index.column() == 8) { // hex column
painter->setFont(hex_font);
} else if (option.state & QStyle::State_Selected || (!bin_view->resize_sig && item->sigs.contains(bin_view->hovered_sig))) {
painter->setPen(Qt::white);
}
painter->drawText(option.rect, Qt::AlignCenter, item->val); painter->drawText(option.rect, Qt::AlignCenter, item->val);
if (item->is_msb || item->is_lsb) { if (item->is_msb || item->is_lsb) {
painter->setFont(small_font); painter->setFont(small_font);

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <QApplication>
#include <QList> #include <QList>
#include <QSet> #include <QSet>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
@ -41,7 +42,7 @@ public:
} }
struct Item { struct Item {
QColor bg_color = QColor(Qt::white); QColor bg_color = QApplication::style()->standardPalette().color(QPalette::Base);
bool is_msb = false; bool is_msb = false;
bool is_lsb = false; bool is_lsb = false;
QString val = "0"; QString val = "0";
@ -79,6 +80,7 @@ private:
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void leaveEvent(QEvent *event) override; void leaveEvent(QEvent *event) override;
void highlightPosition(const QPoint &pt);
QModelIndex anchor_index; QModelIndex anchor_index;
BinaryViewModel *model; BinaryViewModel *model;

@ -1,6 +1,5 @@
#include <QApplication> #include <QApplication>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QStyleFactory>
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
#include "tools/cabana/mainwin.h" #include "tools/cabana/mainwin.h"
@ -8,7 +7,6 @@
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
initApp(argc, argv); initApp(argc, argv);
QApplication app(argc, argv); QApplication app(argc, argv);
app.setStyle(QStyleFactory::create("Fusion"));
QCommandLineParser cmd_parser; QCommandLineParser cmd_parser;
cmd_parser.addHelpOption(); cmd_parser.addHelpOption();

@ -40,6 +40,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
main_layout->addWidget(charts_scroll); main_layout->addWidget(charts_scroll);
use_dark_theme = palette().color(QPalette::WindowText).value() > palette().color(QPalette::Background).value();
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll);
QObject::connect(can, &CANMessages::eventsMerged, this, &ChartsWidget::eventsMerged); QObject::connect(can, &CANMessages::eventsMerged, this, &ChartsWidget::eventsMerged);
QObject::connect(can, &CANMessages::updated, this, &ChartsWidget::updateState); QObject::connect(can, &CANMessages::updated, this, &ChartsWidget::updateState);
@ -125,6 +127,7 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo
ChartView *chart = merge && charts.size() > 0 ? charts.back() : nullptr; ChartView *chart = merge && charts.size() > 0 ? charts.back() : nullptr;
if (!chart) { if (!chart) {
chart = new ChartView(this); chart = new ChartView(this);
chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight);
chart->setEventsRange(display_range); chart->setEventsRange(display_range);
auto range = is_zoomed ? zoomed_range : display_range; auto range = is_zoomed ? zoomed_range : display_range;
chart->setDisplayRange(range.first, range.second); chart->setDisplayRange(range.first, range.second);
@ -288,7 +291,6 @@ void ChartView::updateTitle() {
void ChartView::updateFromSettings() { void ChartView::updateFromSettings() {
setFixedHeight(settings.chart_height); setFixedHeight(settings.chart_height);
chart()->setTheme(settings.chart_theme == 0 ? QChart::ChartThemeLight : QChart::QChart::ChartThemeDark);
} }
void ChartView::setEventsRange(const std::pair<double, double> &range) { void ChartView::setEventsRange(const std::pair<double, double> &range) {

@ -112,4 +112,5 @@ private:
std::pair<double, double> event_range; std::pair<double, double> event_range;
std::pair<double, double> display_range; std::pair<double, double> display_range;
std::pair<double, double> zoomed_range; std::pair<double, double> zoomed_range;
bool use_dark_theme = false;
}; };

@ -25,7 +25,6 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
// tabbar // tabbar
tabbar = new QTabBar(this); tabbar = new QTabBar(this);
tabbar->setTabsClosable(true); tabbar->setTabsClosable(true);
tabbar->setDrawBase(false);
tabbar->setUsesScrollButtons(true); tabbar->setUsesScrollButtons(true);
tabbar->setAutoHide(true); tabbar->setAutoHide(true);
tabbar->setContextMenuPolicy(Qt::CustomContextMenu); tabbar->setContextMenuPolicy(Qt::CustomContextMenu);
@ -57,7 +56,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart
QHBoxLayout *warning_hlayout = new QHBoxLayout(warning_widget); QHBoxLayout *warning_hlayout = new QHBoxLayout(warning_widget);
warning_hlayout->setContentsMargins(0, 0, 0, 0); warning_hlayout->setContentsMargins(0, 0, 0, 0);
QLabel *warning_icon = new QLabel(this); QLabel *warning_icon = new QLabel(this);
warning_icon->setPixmap(style()->standardPixmap(QStyle::SP_MessageBoxWarning)); warning_icon->setPixmap(style()->standardPixmap(QStyle::SP_MessageBoxWarning).scaledToWidth(24, Qt::SmoothTransformation));
warning_hlayout->addWidget(warning_icon, 0, Qt::AlignTop); warning_hlayout->addWidget(warning_icon, 0, Qt::AlignTop);
warning_label = new QLabel(this); warning_label = new QLabel(this);
warning_hlayout->addWidget(warning_label, 1, Qt::AlignLeft); warning_hlayout->addWidget(warning_label, 1, Qt::AlignLeft);

@ -1,6 +1,7 @@
#include "tools/cabana/historylog.h" #include "tools/cabana/historylog.h"
#include <QFontDatabase> #include <QFontDatabase>
#include <QPainter>
// HistoryLogModel // HistoryLogModel
@ -44,6 +45,8 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i
return has_signal ? QString::fromStdString(get_signal(dbc_msg, section - 1).name).replace('_', ' ') : "Data"; return has_signal ? QString::fromStdString(get_signal(dbc_msg, section - 1).name).replace('_', ' ') : "Data";
} else if (role == Qt::BackgroundRole && section > 0 && has_signal) { } else if (role == Qt::BackgroundRole && section > 0 && has_signal) {
return QBrush(QColor(getColor(section - 1))); return QBrush(QColor(getColor(section - 1)));
} else if (role == Qt::ForegroundRole && section > 0 && has_signal) {
return QBrush(Qt::black);
} }
} }
return {}; return {};
@ -73,7 +76,18 @@ void HistoryLogModel::updateState() {
QSize HeaderView::sectionSizeFromContents(int logicalIndex) const { QSize HeaderView::sectionSizeFromContents(int logicalIndex) const {
const QString text = model()->headerData(logicalIndex, this->orientation(), Qt::DisplayRole).toString(); const QString text = model()->headerData(logicalIndex, this->orientation(), Qt::DisplayRole).toString();
const QRect rect = fontMetrics().boundingRect(QRect(0, 0, sectionSize(logicalIndex), 1000), defaultAlignment(), text); const QRect rect = fontMetrics().boundingRect(QRect(0, 0, sectionSize(logicalIndex), 1000), defaultAlignment(), text);
return rect.size() + QSize{10, 5}; return rect.size() + QSize{10, 6};
}
void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const {
auto bg_role = model()->headerData(logicalIndex, Qt::Horizontal, Qt::BackgroundRole);
if (bg_role.isValid()) {
QPen pen(model()->headerData(logicalIndex, Qt::Horizontal, Qt::ForegroundRole).value<QBrush>(), 1);
painter->setPen(pen);
painter->fillRect(rect, bg_role.value<QBrush>());
}
QString text = model()->headerData(logicalIndex, Qt::Horizontal, Qt::DisplayRole).toString();
painter->drawText(rect.adjusted(5, 3, 5, 3), defaultAlignment(), text);
} }
// HistoryLog // HistoryLog
@ -88,7 +102,6 @@ HistoryLog::HistoryLog(QWidget *parent) : QTableView(parent) {
verticalHeader()->setVisible(false); verticalHeader()->setVisible(false);
setFrameShape(QFrame::NoFrame); setFrameShape(QFrame::NoFrame);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
setStyleSheet("QTableView::item { border:0px; padding-left:5px; padding-right:5px; }");
} }
int HistoryLog::sizeHintForColumn(int column) const { int HistoryLog::sizeHintForColumn(int column) const {

@ -10,6 +10,7 @@ class HeaderView : public QHeaderView {
public: public:
HeaderView(Qt::Orientation orientation, QWidget *parent = nullptr) : QHeaderView(orientation, parent) {} HeaderView(Qt::Orientation orientation, QWidget *parent = nullptr) : QHeaderView(orientation, parent) {}
QSize sectionSizeFromContents(int logicalIndex) const override; QSize sectionSizeFromContents(int logicalIndex) const override;
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
}; };
class HistoryLogModel : public QAbstractTableModel { class HistoryLogModel : public QAbstractTableModel {

@ -18,7 +18,6 @@ void Settings::save() {
s.setValue("log_size", can_msg_log_size); s.setValue("log_size", can_msg_log_size);
s.setValue("cached_segment", cached_segment_limit); s.setValue("cached_segment", cached_segment_limit);
s.setValue("chart_height", chart_height); s.setValue("chart_height", chart_height);
s.setValue("chart_theme", chart_theme);
s.setValue("max_chart_x_range", max_chart_x_range); s.setValue("max_chart_x_range", max_chart_x_range);
s.setValue("last_dir", last_dir); s.setValue("last_dir", last_dir);
s.setValue("splitter_state", splitter_state); s.setValue("splitter_state", splitter_state);
@ -30,7 +29,6 @@ void Settings::load() {
can_msg_log_size = s.value("log_size", 50).toInt(); can_msg_log_size = s.value("log_size", 50).toInt();
cached_segment_limit = s.value("cached_segment", 3).toInt(); cached_segment_limit = s.value("cached_segment", 3).toInt();
chart_height = s.value("chart_height", 200).toInt(); chart_height = s.value("chart_height", 200).toInt();
chart_theme = s.value("chart_theme", 0).toInt();
max_chart_x_range = s.value("max_chart_x_range", 3 * 60).toInt(); max_chart_x_range = s.value("max_chart_x_range", 3 * 60).toInt();
last_dir = s.value("last_dir", QDir::homePath()).toString(); last_dir = s.value("last_dir", QDir::homePath()).toString();
splitter_state = s.value("splitter_state").toByteArray(); splitter_state = s.value("splitter_state").toByteArray();
@ -72,11 +70,6 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
chart_height->setValue(settings.chart_height); chart_height->setValue(settings.chart_height);
form_layout->addRow(tr("Chart height"), chart_height); form_layout->addRow(tr("Chart height"), chart_height);
chart_theme = new QComboBox();
chart_theme->addItems({"Light", "Dark"});
chart_theme->setCurrentIndex(settings.chart_theme == 1 ? 1 : 0);
form_layout->addRow(tr("Chart theme"), chart_theme);
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
form_layout->addRow(buttonBox); form_layout->addRow(buttonBox);
@ -90,7 +83,6 @@ void SettingsDlg::save() {
settings.can_msg_log_size = log_size->value(); settings.can_msg_log_size = log_size->value();
settings.cached_segment_limit = cached_segment->value(); settings.cached_segment_limit = cached_segment->value();
settings.chart_height = chart_height->value(); settings.chart_height = chart_height->value();
settings.chart_theme = chart_theme->currentIndex();
settings.max_chart_x_range = max_chart_x_range->value() * 60; settings.max_chart_x_range = max_chart_x_range->value() * 60;
settings.save(); settings.save();
accept(); accept();

@ -17,7 +17,6 @@ public:
int can_msg_log_size = 50; int can_msg_log_size = 50;
int cached_segment_limit = 3; int cached_segment_limit = 3;
int chart_height = 200; int chart_height = 200;
int chart_theme = 0;
int max_chart_x_range = 3 * 60; // 3 minutes int max_chart_x_range = 3 * 60; // 3 minutes
QString last_dir; QString last_dir;
QByteArray splitter_state; QByteArray splitter_state;
@ -36,7 +35,6 @@ public:
QSpinBox *log_size ; QSpinBox *log_size ;
QSpinBox *cached_segment; QSpinBox *cached_segment;
QSpinBox *chart_height; QSpinBox *chart_height;
QComboBox *chart_theme;
QSpinBox *max_chart_x_range; QSpinBox *max_chart_x_range;
}; };

@ -137,7 +137,7 @@ void SignalEdit::setSignal(const QString &message_id, const Signal *signal) {
updateForm(msg_id == message_id && form->isVisible()); updateForm(msg_id == message_id && form->isVisible());
msg_id = message_id; msg_id = message_id;
color_label->setText(QString::number(form_idx + 1)); color_label->setText(QString::number(form_idx + 1));
color_label->setStyleSheet(QString("background-color:%1").arg(getColor(form_idx))); color_label->setStyleSheet(QString("color:black; background-color:%2").arg(getColor(form_idx)));
title->setText(sig->name.c_str()); title->setText(sig->name.c_str());
show(); show();
} }

Loading…
Cancel
Save