|
|
|
from collections import defaultdict, deque
|
|
|
|
import pytest
|
|
|
|
import time
|
|
|
|
import numpy as np
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from tabulate import tabulate
|
|
|
|
|
|
|
|
import cereal.messaging as messaging
|
|
|
|
from cereal.services import SERVICE_LIST
|
|
|
|
from opendbc.car.car_helpers import get_demo_car_params
|
|
|
|
from openpilot.common.mock import mock_messages
|
|
|
|
from openpilot.common.params import Params
|
selfdrive/car: ban cereal and capnp (#33208)
* ban cereal and msgq
* common too
* do toyota/values.py
* do all fingerprints
* example without builder
* this still works, but no type checking anymore
* stash
* wtf, how does this work
* okay actually not bad
* safe
* epic!
* stash data_structures.py
* some clean up
* hell yeah
* clean up old file
* add to delete
* delete
This reverts commit 90239b7797ace31ee647a2fdbd67e0c3faa98dcf.
* switch more CarParams stuff over
remove unused
* fix car tests by removing cereal! mypy forgets about dataclass if we wrap it :(
* fix this too
* fix this too
* remove more cereal and add some good hyundai tests
* bunch more typing
* override default with 20hz radar
* temp capnp converter helper
* more lateralTuning
* small union replicator is better than what i was trying, and fixes mypy dynamic typing issues
* can keep all this the same now!
* type ret: CarParams, add more missing structs, revert lateralTuning changes (smaller diff!)
* revert more
* get first enum automatically, but ofc mypy doesn't pick up the new metaclass so can't use :(
would have been `CarParams.NetworkLocation()`
* Revert "get first enum automatically, but ofc mypy doesn't pick up the new metaclass so can't use :("
This reverts commit bb28b228becba932052d2fc5a4389784027435b1.
* remove cereal from car_helpers (TODO: caching)
* remove a bunch of temp lines
* use dataclass_transform!
* remove some car.CarParams from the interfaces
* remove rest of car.CarParams from the interfaces
* same which() API
* sort
* from cereal/cache from fingerprinting!
* more typing
* dataclass to capnp helper for CarParams, cached it since it's kinda slow
* (partial) fix process replay fingerprintig for new API
* latcontrollers take capnp
* forgot this
* fix test_models
* fix unit tests
* not here
* VehicleModel and controller still takes capnp CP since they get it from Params()
* fix modeld test
* more fix
* need to namespace to structs, since CarState is both class and struct
* this was never in the base class?!
* clean that up again
* fix import error
fix import error
* cmts and more structs
* remove some more cereal from toyota + convert CarState to capnp
* bruh this was wrong
* replace more cereal
* EventName is one of the last things...
* replace a bunch more cereal.car
* missing imports
* more
* can fix this typing now
* proper toyota+others CS typing!
* mypy can detect return type of CS.update() now
* fix redeclaration of cruise_buttons type
* mypy is only complaining about events now
* temp fix
* add carControl struct
* replace CarControl
i hope there's no circular imports in hyundai's CC
* fine now
* lol this was wrong too
* fix crash
* include my failed attempts at recursively converting to dataclass (doesn't implicitly convert types/recursively :( )
but attrs does, maybe will switch in the future
* clean up
* try out attr.s for its converter (doesn't work recursively yet, but interesting!)
* Revert "try out attr.s for its converter (doesn't work recursively yet, but interesting!)"
This reverts commit ff2434f7bbd45a4d4bfb21f7d6712d1f1c3bcde9.
* test processes doesn't fail anymore (on toyota)!
* fix honda crash
* stash
* Revert "stash"
This reverts commit c1762af4e776790e4ad1322ad4ce0610157346e0.
* remove a bunch more cereal!
* LET'S GOOO
* fix these tests
* and these
* and that
* stash, something is wrong with hyundai enable
* Revert "stash, something is wrong with hyundai enable"
This reverts commit 39cf327def258e2959fe23cd7a550a858f6d8f03.
* forgot these
* remove cereal from fw_versions
* Revert "remove cereal from fw_versions"
This reverts commit 232b37cd409b55d04b1afc90d4a80c49e710eb56.
* remove rest of the cereal exceptions!
* fix that
* add typing to radard since I didn't realize RI.update() switched from cereal to structs
* and here too!
* add TODO for slots
* needed CS to be capnp, fix comparisons, and type hint car_specific so it's easier to catch type issues (capnp isn't detected by mypy :( )
* remove the struct converter
* save ~4-5% CPU at 100hz, we don't modify after so no need to deepcopy
btw pickle.loads(pickle.dumps()) is faster by ~1% CPU
* deepcopy -> copy: we can technically make a reference, but copy is almost free and less error-prone
saves ~1% CPU
* add non-copying asdict function
* should save ~3% CPU (still 4% above baseline)
* fix that, no dict support
* ~27% decrease in time for 20k iterations on 3X (3.37857 -> 2.4821s)
* give a better name
* fix
* dont support none, capitalize
* sheesh, this called type() on every field
* remove CS.events, clean up
* bump card %
* this was a bug on master!
* add a which enum
* default to pid
* revert
* update refs
* not needed, but consistent
* just Ecu
* don't need to do this in this pr
* clean up
* no cast
* consistent typing
* rm
* fix
* can do this if we're desperate for the last few %
* Revert "can do this if we're desperate for the last few %"
This reverts commit 18e11ac7883a0a56583750b1cc5a2b13011e7299.
* type this
* don't need to convert carControl
* i guess don't support set either
* fix CP type hint
* simplify that
old-commit-hash: 6a15c42143a4764e79df851d797f950a6212f464
1 year ago
|
|
|
from openpilot.selfdrive.car.card import convert_to_capnp
|
|
|
|
from openpilot.system.hardware.tici.power_monitor import get_power
|
|
|
|
from openpilot.system.manager.process_config import managed_processes
|
|
|
|
from openpilot.system.manager.manager import manager_cleanup
|
|
|
|
|
|
|
|
SAMPLE_TIME = 8 # seconds to sample power
|
|
|
|
MAX_WARMUP_TIME = 30 # seconds to wait for SAMPLE_TIME consecutive valid samples
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Proc:
|
|
|
|
procs: list[str]
|
|
|
|
power: float
|
|
|
|
msgs: list[str]
|
|
|
|
rtol: float = 0.05
|
|
|
|
atol: float = 0.12
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
return '+'.join(self.procs)
|
|
|
|
|
|
|
|
|
|
|
|
PROCS = [
|
|
|
|
Proc(['camerad'], 2.1, msgs=['roadCameraState', 'wideRoadCameraState', 'driverCameraState']),
|
|
|
|
Proc(['modeld'], 1.12, atol=0.2, msgs=['modelV2']),
|
|
|
|
Proc(['dmonitoringmodeld'], 0.4, msgs=['driverStateV2']),
|
|
|
|
Proc(['encoderd'], 0.23, msgs=[]),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.tici
|
|
|
|
class TestPowerDraw:
|
|
|
|
|
|
|
|
def setup_method(self):
|
selfdrive/car: ban cereal and capnp (#33208)
* ban cereal and msgq
* common too
* do toyota/values.py
* do all fingerprints
* example without builder
* this still works, but no type checking anymore
* stash
* wtf, how does this work
* okay actually not bad
* safe
* epic!
* stash data_structures.py
* some clean up
* hell yeah
* clean up old file
* add to delete
* delete
This reverts commit 90239b7797ace31ee647a2fdbd67e0c3faa98dcf.
* switch more CarParams stuff over
remove unused
* fix car tests by removing cereal! mypy forgets about dataclass if we wrap it :(
* fix this too
* fix this too
* remove more cereal and add some good hyundai tests
* bunch more typing
* override default with 20hz radar
* temp capnp converter helper
* more lateralTuning
* small union replicator is better than what i was trying, and fixes mypy dynamic typing issues
* can keep all this the same now!
* type ret: CarParams, add more missing structs, revert lateralTuning changes (smaller diff!)
* revert more
* get first enum automatically, but ofc mypy doesn't pick up the new metaclass so can't use :(
would have been `CarParams.NetworkLocation()`
* Revert "get first enum automatically, but ofc mypy doesn't pick up the new metaclass so can't use :("
This reverts commit bb28b228becba932052d2fc5a4389784027435b1.
* remove cereal from car_helpers (TODO: caching)
* remove a bunch of temp lines
* use dataclass_transform!
* remove some car.CarParams from the interfaces
* remove rest of car.CarParams from the interfaces
* same which() API
* sort
* from cereal/cache from fingerprinting!
* more typing
* dataclass to capnp helper for CarParams, cached it since it's kinda slow
* (partial) fix process replay fingerprintig for new API
* latcontrollers take capnp
* forgot this
* fix test_models
* fix unit tests
* not here
* VehicleModel and controller still takes capnp CP since they get it from Params()
* fix modeld test
* more fix
* need to namespace to structs, since CarState is both class and struct
* this was never in the base class?!
* clean that up again
* fix import error
fix import error
* cmts and more structs
* remove some more cereal from toyota + convert CarState to capnp
* bruh this was wrong
* replace more cereal
* EventName is one of the last things...
* replace a bunch more cereal.car
* missing imports
* more
* can fix this typing now
* proper toyota+others CS typing!
* mypy can detect return type of CS.update() now
* fix redeclaration of cruise_buttons type
* mypy is only complaining about events now
* temp fix
* add carControl struct
* replace CarControl
i hope there's no circular imports in hyundai's CC
* fine now
* lol this was wrong too
* fix crash
* include my failed attempts at recursively converting to dataclass (doesn't implicitly convert types/recursively :( )
but attrs does, maybe will switch in the future
* clean up
* try out attr.s for its converter (doesn't work recursively yet, but interesting!)
* Revert "try out attr.s for its converter (doesn't work recursively yet, but interesting!)"
This reverts commit ff2434f7bbd45a4d4bfb21f7d6712d1f1c3bcde9.
* test processes doesn't fail anymore (on toyota)!
* fix honda crash
* stash
* Revert "stash"
This reverts commit c1762af4e776790e4ad1322ad4ce0610157346e0.
* remove a bunch more cereal!
* LET'S GOOO
* fix these tests
* and these
* and that
* stash, something is wrong with hyundai enable
* Revert "stash, something is wrong with hyundai enable"
This reverts commit 39cf327def258e2959fe23cd7a550a858f6d8f03.
* forgot these
* remove cereal from fw_versions
* Revert "remove cereal from fw_versions"
This reverts commit 232b37cd409b55d04b1afc90d4a80c49e710eb56.
* remove rest of the cereal exceptions!
* fix that
* add typing to radard since I didn't realize RI.update() switched from cereal to structs
* and here too!
* add TODO for slots
* needed CS to be capnp, fix comparisons, and type hint car_specific so it's easier to catch type issues (capnp isn't detected by mypy :( )
* remove the struct converter
* save ~4-5% CPU at 100hz, we don't modify after so no need to deepcopy
btw pickle.loads(pickle.dumps()) is faster by ~1% CPU
* deepcopy -> copy: we can technically make a reference, but copy is almost free and less error-prone
saves ~1% CPU
* add non-copying asdict function
* should save ~3% CPU (still 4% above baseline)
* fix that, no dict support
* ~27% decrease in time for 20k iterations on 3X (3.37857 -> 2.4821s)
* give a better name
* fix
* dont support none, capitalize
* sheesh, this called type() on every field
* remove CS.events, clean up
* bump card %
* this was a bug on master!
* add a which enum
* default to pid
* revert
* update refs
* not needed, but consistent
* just Ecu
* don't need to do this in this pr
* clean up
* no cast
* consistent typing
* rm
* fix
* can do this if we're desperate for the last few %
* Revert "can do this if we're desperate for the last few %"
This reverts commit 18e11ac7883a0a56583750b1cc5a2b13011e7299.
* type this
* don't need to convert carControl
* i guess don't support set either
* fix CP type hint
* simplify that
old-commit-hash: 6a15c42143a4764e79df851d797f950a6212f464
1 year ago
|
|
|
Params().put("CarParams", convert_to_capnp(get_demo_car_params()).to_bytes())
|
|
|
|
|
|
|
|
# wait a bit for power save to disable
|
|
|
|
time.sleep(5)
|
|
|
|
|
|
|
|
def teardown_method(self):
|
|
|
|
manager_cleanup()
|
|
|
|
|
|
|
|
def get_expected_messages(self, proc):
|
|
|
|
return int(sum(SAMPLE_TIME * SERVICE_LIST[msg].frequency for msg in proc.msgs))
|
|
|
|
|
|
|
|
def valid_msg_count(self, proc, msg_counts):
|
|
|
|
msgs_received = sum(msg_counts[msg] for msg in proc.msgs)
|
|
|
|
msgs_expected = self.get_expected_messages(proc)
|
|
|
|
return np.core.numeric.isclose(msgs_expected, msgs_received, rtol=.02, atol=2)
|
|
|
|
|
|
|
|
def valid_power_draw(self, proc, used):
|
|
|
|
return np.core.numeric.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol)
|
|
|
|
|
|
|
|
def tabulate_msg_counts(self, msgs_and_power):
|
|
|
|
msg_counts = defaultdict(int)
|
|
|
|
for _, counts in msgs_and_power:
|
|
|
|
for msg, count in counts.items():
|
|
|
|
msg_counts[msg] += count
|
|
|
|
return msg_counts
|
|
|
|
|
|
|
|
def get_power_with_warmup_for_target(self, proc, prev):
|
|
|
|
socks = {msg: messaging.sub_sock(msg) for msg in proc.msgs}
|
|
|
|
for sock in socks.values():
|
|
|
|
messaging.drain_sock_raw(sock)
|
|
|
|
|
|
|
|
msgs_and_power = deque([], maxlen=SAMPLE_TIME)
|
|
|
|
|
|
|
|
start_time = time.monotonic()
|
|
|
|
|
|
|
|
while (time.monotonic() - start_time) < MAX_WARMUP_TIME:
|
|
|
|
power = get_power(1)
|
|
|
|
iteration_msg_counts = {}
|
|
|
|
for msg,sock in socks.items():
|
|
|
|
iteration_msg_counts[msg] = len(messaging.drain_sock_raw(sock))
|
|
|
|
msgs_and_power.append((power, iteration_msg_counts))
|
|
|
|
|
|
|
|
if len(msgs_and_power) < SAMPLE_TIME:
|
|
|
|
continue
|
|
|
|
|
|
|
|
msg_counts = self.tabulate_msg_counts(msgs_and_power)
|
|
|
|
now = np.mean([m[0] for m in msgs_and_power])
|
|
|
|
|
|
|
|
if self.valid_msg_count(proc, msg_counts) and self.valid_power_draw(proc, now - prev):
|
|
|
|
break
|
|
|
|
|
|
|
|
return now, msg_counts, time.monotonic() - start_time - SAMPLE_TIME
|
|
|
|
|
|
|
|
@mock_messages(['livePose'])
|
|
|
|
def test_camera_procs(self, subtests):
|
|
|
|
baseline = get_power()
|
|
|
|
|
|
|
|
prev = baseline
|
|
|
|
used = {}
|
|
|
|
warmup_time = {}
|
|
|
|
msg_counts = {}
|
|
|
|
|
|
|
|
for proc in PROCS:
|
|
|
|
for p in proc.procs:
|
|
|
|
managed_processes[p].start()
|
|
|
|
now, local_msg_counts, warmup_time[proc.name] = self.get_power_with_warmup_for_target(proc, prev)
|
|
|
|
msg_counts.update(local_msg_counts)
|
|
|
|
|
|
|
|
used[proc.name] = now - prev
|
|
|
|
prev = now
|
|
|
|
|
|
|
|
manager_cleanup()
|
|
|
|
|
|
|
|
tab = [['process', 'expected (W)', 'measured (W)', '# msgs expected', '# msgs received', "warmup time (s)"]]
|
|
|
|
for proc in PROCS:
|
|
|
|
cur = used[proc.name]
|
|
|
|
expected = proc.power
|
|
|
|
msgs_received = sum(msg_counts[msg] for msg in proc.msgs)
|
|
|
|
tab.append([proc.name, round(expected, 2), round(cur, 2), self.get_expected_messages(proc), msgs_received, round(warmup_time[proc.name], 2)])
|
|
|
|
with subtests.test(proc=proc.name):
|
|
|
|
assert self.valid_msg_count(proc, msg_counts), f"expected {self.get_expected_messages(proc)} msgs, got {msgs_received} msgs"
|
|
|
|
assert self.valid_power_draw(proc, cur), f"expected {expected:.2f}W, got {cur:.2f}W"
|
|
|
|
print(tabulate(tab))
|
|
|
|
print(f"Baseline {baseline:.2f}W\n")
|