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 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
8 months ago
from openpilot . selfdrive . car . card import convert_to_capnp
from openpilot . selfdrive . car . car_helpers import get_demo_car_params
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
8 months 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 " )