#!/usr/bin/env python3 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								import  pytest 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								import  random 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								import  time 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								from  collections  import  defaultdict 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								from  parameterized  import  parameterized 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  cereal  import  car 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								from  openpilot . selfdrive . car . car_helpers  import  interfaces 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								from  openpilot . selfdrive . car . fingerprints  import  FW_VERSIONS 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								from  openpilot . selfdrive . car . fw_versions  import  ESSENTIAL_ECUS ,  FW_QUERY_CONFIGS ,  FUZZY_EXCLUDE_ECUS ,  VERSIONS ,  build_fw_dict ,  \
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								                                                match_fw_to_car ,  get_brand_ecu_matches ,  get_fw_versions ,  get_present_ecus 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								from  openpilot . selfdrive . car . vin  import  get_vin 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								CarFw  =  car . CarParams . CarFw 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								Ecu  =  car . CarParams . Ecu 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								ECU_NAME  =  { v :  k  for  k ,  v  in  Ecu . schema . enumerants . items ( ) } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								class  FakeSocket : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  receive ( self ,  non_blocking = False ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    pass 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  send ( self ,  msg ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    pass 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								class  TestFwFingerprint : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  assertFingerprints ( self ,  candidates ,  expected ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    candidates  =  list ( candidates ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    assert  len ( candidates )  ==  1 ,  f " got more than one candidate:  { candidates } " 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    assert  candidates [ 0 ]  ==  expected 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  @parameterized . expand ( [ ( b ,  c ,  e [ c ] ,  n )  for  b ,  e  in  VERSIONS . items ( )  for  c  in  e  for  n  in  ( True ,  False ) ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  test_exact_match ( self ,  brand ,  car_model ,  ecus ,  test_non_essential ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    config  =  FW_QUERY_CONFIGS [ brand ] 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    CP  =  car . CarParams . new_message ( ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    for  _  in  range ( 100 ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      fw  =  [ ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  ecu ,  fw_versions  in  ecus . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        # Assume non-essential ECUs apply to all cars, so we catch cases where Car A with 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # missing ECUs won't match to Car B where only Car B has labeled non-essential ECUs 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ecu [ 0 ]  in  config . non_essential_ecus  and  test_non_essential : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          continue 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ecu_name ,  addr ,  sub_addr  =  ecu 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        fw . append ( { " ecu " :  ecu_name ,  " fwVersion " :  random . choice ( fw_versions ) ,  ' brand ' :  brand , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								                   " address " :  addr ,  " subAddress " :  0  if  sub_addr  is  None  else  sub_addr } ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      CP . carFw  =  fw 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      _ ,  matches  =  match_fw_to_car ( CP . carFw ,  CP . carVin ,  allow_fuzzy = False ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      if  not  test_non_essential : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . assertFingerprints ( matches ,  car_model ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      else : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # if we're removing ECUs we expect some match loss, but it shouldn't mismatch 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  len ( matches )  !=  0 : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          self . assertFingerprints ( matches ,  car_model ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												HKG: use platform codes to fuzzy fingerprint (#28531)
* get gas/ev/hev from FW (not all correct, poc)
* add test for essential ecus for fuzzy fingerprinting
* kinda works
* stash
* clean up
* add code
* simpler
* use the function
* test it with our cars
* no re
no re
no re
* debugging
* handle empty dict
* simpl
* this is promising
start on making existing fingerprinting functions use the config, instead of entirely replacing them
* needs to allow 1 match
* lay out how this should look
* changes
* executable
* some work
* use config
* fuzzy ecus
* config test
* comment and some clean up
* test platform codes
* use regex, simpler and fixes bug
* in func
* rm bad func
* typing for new func and remove old from dc
* todo done
* tested!
* remove fake platform codes
* thought we needed this, but actually...
* not needed
* not applicable any more
* use config for essential ecus
* first draft of test to make adding/removing fuzzy FP platform intentional
* compile
* clean up test
* even cleaner
* fix default ecus type
* temp fix
* this is mostly in tests now
* test every fuzzy ecu fw returns one platform code
* experiment with dates
* Revert "experiment with dates"
This reverts commit 3251b9cc5c3ca41ca92c8b75ad9b2234b720aa0b.
* clean that up
* comment
* test
* work on all cars
* fix fuzz_fw_fingerprint
* comment
* get first by search
* bit more clean up
* and more
* use compiled pattern for nicer syntax
* default
* flip dat around, much cleaner
* clean up hyundai test a bit
* flip order
same here
* rename test and flip subTest
* fix pylint
* revert fw changes
revert fw changes
* line
* add original functions to test
* needs to be a list
* cmt
* draft (need to count one ecu as a match)
* tiny clean up
* todo: date range
* only in notebook
* remove comment (still can be either list or set)
* same, only notebook
* more consistent signature
* copilot inspired
* copilot no good
* test for date parsing
* better name
* good, now we don't have to worry about the dates mismatching in another test/logic
* comment up+
* some stuff
* clean up
fix test
fix test
* test
* comment
* use utils
* clean up (utils are cleaner and less buggy)
* clean up (utils are cleaner and less buggy)
* fixup test
* use a dash (prettier) and remove some platforms that can fingerprint now!
* compile global pattern
* same as what we do in values
* remove comments
* fuzzy_get_platform_codes is one or none here
* more clean up
* sort imports
* woah woah woah
* add comment
* fix reassigning different types
* add types
* adapt fuzzy test recently added (nice it caught this!)
* update lock
* options
comments
* stash
* comments and fixes
* better comments
* better
* test: run on exact fuzzy matching logic, same results!
* use match_fw_to_car
* test all fw
* ex
* unused random
* this is a possibility
* this is more clear
* fix
* revert
* revert to needing both ECUs to match to reduce false positives, test
* fix excluded platform test :( but it's ok
* add comment
* we actually want to only test fuzzy ecus to make it explicit
* fix mypy
* comment for tomorrow
* just add matches with fuzzy FP
* add comment
* this was the cleanest I could think of, but still working on it. not very easy to understand
* think this is better, but also worse...
* comment: reframing how this works
* revert back to what we did before
* was swapped
* else set
* remove old comment
* fixes from merge
* remove fuzzy_min_match_count from this pr
* fix static analysis
* also unused
* different method first draft
* copy existing fuzzy func
* check all possible ecus exist, only platform codes, slightly refactor main loop
* fix
* Revert recent
Revert "fix"
This reverts commit 5cdb7bda835f1e48e750ab4195e891afe08e11ea.
Revert "check all possible ecus exist, only platform codes, slightly refactor main loop"
This reverts commit d3e918fa20fa4ce881445850f5f7428a3c11adf8.
Revert "copy existing fuzzy func"
This reverts commit 34c8c0545097c84f55f4b4f61907606c93760ddd.
Revert "different method first draft"
This reverts commit b91139055d7d1802c1eb726504798c156a183c9c.
* new func
* fixup test
* remove changes from v1 from fw_versions.py
* clean up a bit
* return part as part of code
* fix test
* add original fuzzy function
* add an ecu match if the date is within range (or date doesn't exist)
* add format for what we're going to do
* not working stash
* the exact matching function does more of what we want with less code and less custom logic
* we don't care about found versions, only codes and dates
* actually we do have an exception
* this works pretty nicely now
* up here
* this is better
* some minor clean up
* old function=now junk
* fix platform code test
* remove old platform code function
* now rename _new to
* use FW_QUERY_CONFIG
* clean up imports
* rename that too
* one line
* correct typing
correct typing
* draft tests
* so that works
* fixup excluded platform test now too
* this is tested by excluded platform test
* test parts and dates
* remove old comment
* old import
* take platform code stuff out of FwQueryConfig
* fix test
* revert debug script
* flip order
* make this a set by default
* revert this part
* correct typing
* clean up comments
* clean that test up too/pylint
* combine these three tests ina clean way
* not right
* more general
* be consistent with quotes
* comment
* comment
* comment in fw_versions
* flip order
* this is more readable
* could test all this, but it's tested in test_hyundai and doesn't do a lot here
* only assert brands which use this
* invalidate all CAN FD ICE and hybrid
* tuple
* can get away without filtering
* add comment reasons
* fix
* some review suggestions
* this works (first draft)
* this is better
* script to print platform codes and dates
* sanity check for dates are in correct ecus and platforms
* mypy
* better variable name and comment
* rename
* same
* slightly better name
* subset
* exclude platforms and live car without dates
* consistent
* self explan
* better name
* test to make sure the functions agree
* clean that up
* comment
* we get other responses from queries not in DB, only check any
* not used or typed
old-commit-hash: f788edb6a5c8d3f516076f886fe0d831ee43b580
											 
										 
										
											2 years ago 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  @parameterized . expand ( [ ( b ,  c ,  e [ c ] )  for  b ,  e  in  VERSIONS . items ( )  for  c  in  e ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  test_custom_fuzzy_match ( self ,  brand ,  car_model ,  ecus ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # Assert brand-specific fuzzy fingerprinting function doesn't disagree with standard fuzzy function 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    config  =  FW_QUERY_CONFIGS [ brand ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  config . match_fw_to_car_fuzzy  is  None : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      pytest . skip ( " Brand does not implement custom fuzzy fingerprinting function " ) 
 
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												HKG: use platform codes to fuzzy fingerprint (#28531)
* get gas/ev/hev from FW (not all correct, poc)
* add test for essential ecus for fuzzy fingerprinting
* kinda works
* stash
* clean up
* add code
* simpler
* use the function
* test it with our cars
* no re
no re
no re
* debugging
* handle empty dict
* simpl
* this is promising
start on making existing fingerprinting functions use the config, instead of entirely replacing them
* needs to allow 1 match
* lay out how this should look
* changes
* executable
* some work
* use config
* fuzzy ecus
* config test
* comment and some clean up
* test platform codes
* use regex, simpler and fixes bug
* in func
* rm bad func
* typing for new func and remove old from dc
* todo done
* tested!
* remove fake platform codes
* thought we needed this, but actually...
* not needed
* not applicable any more
* use config for essential ecus
* first draft of test to make adding/removing fuzzy FP platform intentional
* compile
* clean up test
* even cleaner
* fix default ecus type
* temp fix
* this is mostly in tests now
* test every fuzzy ecu fw returns one platform code
* experiment with dates
* Revert "experiment with dates"
This reverts commit 3251b9cc5c3ca41ca92c8b75ad9b2234b720aa0b.
* clean that up
* comment
* test
* work on all cars
* fix fuzz_fw_fingerprint
* comment
* get first by search
* bit more clean up
* and more
* use compiled pattern for nicer syntax
* default
* flip dat around, much cleaner
* clean up hyundai test a bit
* flip order
same here
* rename test and flip subTest
* fix pylint
* revert fw changes
revert fw changes
* line
* add original functions to test
* needs to be a list
* cmt
* draft (need to count one ecu as a match)
* tiny clean up
* todo: date range
* only in notebook
* remove comment (still can be either list or set)
* same, only notebook
* more consistent signature
* copilot inspired
* copilot no good
* test for date parsing
* better name
* good, now we don't have to worry about the dates mismatching in another test/logic
* comment up+
* some stuff
* clean up
fix test
fix test
* test
* comment
* use utils
* clean up (utils are cleaner and less buggy)
* clean up (utils are cleaner and less buggy)
* fixup test
* use a dash (prettier) and remove some platforms that can fingerprint now!
* compile global pattern
* same as what we do in values
* remove comments
* fuzzy_get_platform_codes is one or none here
* more clean up
* sort imports
* woah woah woah
* add comment
* fix reassigning different types
* add types
* adapt fuzzy test recently added (nice it caught this!)
* update lock
* options
comments
* stash
* comments and fixes
* better comments
* better
* test: run on exact fuzzy matching logic, same results!
* use match_fw_to_car
* test all fw
* ex
* unused random
* this is a possibility
* this is more clear
* fix
* revert
* revert to needing both ECUs to match to reduce false positives, test
* fix excluded platform test :( but it's ok
* add comment
* we actually want to only test fuzzy ecus to make it explicit
* fix mypy
* comment for tomorrow
* just add matches with fuzzy FP
* add comment
* this was the cleanest I could think of, but still working on it. not very easy to understand
* think this is better, but also worse...
* comment: reframing how this works
* revert back to what we did before
* was swapped
* else set
* remove old comment
* fixes from merge
* remove fuzzy_min_match_count from this pr
* fix static analysis
* also unused
* different method first draft
* copy existing fuzzy func
* check all possible ecus exist, only platform codes, slightly refactor main loop
* fix
* Revert recent
Revert "fix"
This reverts commit 5cdb7bda835f1e48e750ab4195e891afe08e11ea.
Revert "check all possible ecus exist, only platform codes, slightly refactor main loop"
This reverts commit d3e918fa20fa4ce881445850f5f7428a3c11adf8.
Revert "copy existing fuzzy func"
This reverts commit 34c8c0545097c84f55f4b4f61907606c93760ddd.
Revert "different method first draft"
This reverts commit b91139055d7d1802c1eb726504798c156a183c9c.
* new func
* fixup test
* remove changes from v1 from fw_versions.py
* clean up a bit
* return part as part of code
* fix test
* add original fuzzy function
* add an ecu match if the date is within range (or date doesn't exist)
* add format for what we're going to do
* not working stash
* the exact matching function does more of what we want with less code and less custom logic
* we don't care about found versions, only codes and dates
* actually we do have an exception
* this works pretty nicely now
* up here
* this is better
* some minor clean up
* old function=now junk
* fix platform code test
* remove old platform code function
* now rename _new to
* use FW_QUERY_CONFIG
* clean up imports
* rename that too
* one line
* correct typing
correct typing
* draft tests
* so that works
* fixup excluded platform test now too
* this is tested by excluded platform test
* test parts and dates
* remove old comment
* old import
* take platform code stuff out of FwQueryConfig
* fix test
* revert debug script
* flip order
* make this a set by default
* revert this part
* correct typing
* clean up comments
* clean that test up too/pylint
* combine these three tests ina clean way
* not right
* more general
* be consistent with quotes
* comment
* comment
* comment in fw_versions
* flip order
* this is more readable
* could test all this, but it's tested in test_hyundai and doesn't do a lot here
* only assert brands which use this
* invalidate all CAN FD ICE and hybrid
* tuple
* can get away without filtering
* add comment reasons
* fix
* some review suggestions
* this works (first draft)
* this is better
* script to print platform codes and dates
* sanity check for dates are in correct ecus and platforms
* mypy
* better variable name and comment
* rename
* same
* slightly better name
* subset
* exclude platforms and live car without dates
* consistent
* self explan
* better name
* test to make sure the functions agree
* clean that up
* comment
* we get other responses from queries not in DB, only check any
* not used or typed
old-commit-hash: f788edb6a5c8d3f516076f886fe0d831ee43b580
											 
										 
										
											2 years ago 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    CP  =  car . CarParams . new_message ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  _  in  range ( 5 ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      fw  =  [ ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  ecu ,  fw_versions  in  ecus . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        ecu_name ,  addr ,  sub_addr  =  ecu 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        fw . append ( { " ecu " :  ecu_name ,  " fwVersion " :  random . choice ( fw_versions ) ,  ' brand ' :  brand , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                   " address " :  addr ,  " subAddress " :  0  if  sub_addr  is  None  else  sub_addr } ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      CP . carFw  =  fw 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      _ ,  matches  =  match_fw_to_car ( CP . carFw ,  CP . carVin ,  allow_exact = False ,  log = False ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      brand_matches  =  config . match_fw_to_car_fuzzy ( build_fw_dict ( CP . carFw ) ,  CP . carVin ,  VERSIONS [ brand ] ) 
 
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												HKG: use platform codes to fuzzy fingerprint (#28531)
* get gas/ev/hev from FW (not all correct, poc)
* add test for essential ecus for fuzzy fingerprinting
* kinda works
* stash
* clean up
* add code
* simpler
* use the function
* test it with our cars
* no re
no re
no re
* debugging
* handle empty dict
* simpl
* this is promising
start on making existing fingerprinting functions use the config, instead of entirely replacing them
* needs to allow 1 match
* lay out how this should look
* changes
* executable
* some work
* use config
* fuzzy ecus
* config test
* comment and some clean up
* test platform codes
* use regex, simpler and fixes bug
* in func
* rm bad func
* typing for new func and remove old from dc
* todo done
* tested!
* remove fake platform codes
* thought we needed this, but actually...
* not needed
* not applicable any more
* use config for essential ecus
* first draft of test to make adding/removing fuzzy FP platform intentional
* compile
* clean up test
* even cleaner
* fix default ecus type
* temp fix
* this is mostly in tests now
* test every fuzzy ecu fw returns one platform code
* experiment with dates
* Revert "experiment with dates"
This reverts commit 3251b9cc5c3ca41ca92c8b75ad9b2234b720aa0b.
* clean that up
* comment
* test
* work on all cars
* fix fuzz_fw_fingerprint
* comment
* get first by search
* bit more clean up
* and more
* use compiled pattern for nicer syntax
* default
* flip dat around, much cleaner
* clean up hyundai test a bit
* flip order
same here
* rename test and flip subTest
* fix pylint
* revert fw changes
revert fw changes
* line
* add original functions to test
* needs to be a list
* cmt
* draft (need to count one ecu as a match)
* tiny clean up
* todo: date range
* only in notebook
* remove comment (still can be either list or set)
* same, only notebook
* more consistent signature
* copilot inspired
* copilot no good
* test for date parsing
* better name
* good, now we don't have to worry about the dates mismatching in another test/logic
* comment up+
* some stuff
* clean up
fix test
fix test
* test
* comment
* use utils
* clean up (utils are cleaner and less buggy)
* clean up (utils are cleaner and less buggy)
* fixup test
* use a dash (prettier) and remove some platforms that can fingerprint now!
* compile global pattern
* same as what we do in values
* remove comments
* fuzzy_get_platform_codes is one or none here
* more clean up
* sort imports
* woah woah woah
* add comment
* fix reassigning different types
* add types
* adapt fuzzy test recently added (nice it caught this!)
* update lock
* options
comments
* stash
* comments and fixes
* better comments
* better
* test: run on exact fuzzy matching logic, same results!
* use match_fw_to_car
* test all fw
* ex
* unused random
* this is a possibility
* this is more clear
* fix
* revert
* revert to needing both ECUs to match to reduce false positives, test
* fix excluded platform test :( but it's ok
* add comment
* we actually want to only test fuzzy ecus to make it explicit
* fix mypy
* comment for tomorrow
* just add matches with fuzzy FP
* add comment
* this was the cleanest I could think of, but still working on it. not very easy to understand
* think this is better, but also worse...
* comment: reframing how this works
* revert back to what we did before
* was swapped
* else set
* remove old comment
* fixes from merge
* remove fuzzy_min_match_count from this pr
* fix static analysis
* also unused
* different method first draft
* copy existing fuzzy func
* check all possible ecus exist, only platform codes, slightly refactor main loop
* fix
* Revert recent
Revert "fix"
This reverts commit 5cdb7bda835f1e48e750ab4195e891afe08e11ea.
Revert "check all possible ecus exist, only platform codes, slightly refactor main loop"
This reverts commit d3e918fa20fa4ce881445850f5f7428a3c11adf8.
Revert "copy existing fuzzy func"
This reverts commit 34c8c0545097c84f55f4b4f61907606c93760ddd.
Revert "different method first draft"
This reverts commit b91139055d7d1802c1eb726504798c156a183c9c.
* new func
* fixup test
* remove changes from v1 from fw_versions.py
* clean up a bit
* return part as part of code
* fix test
* add original fuzzy function
* add an ecu match if the date is within range (or date doesn't exist)
* add format for what we're going to do
* not working stash
* the exact matching function does more of what we want with less code and less custom logic
* we don't care about found versions, only codes and dates
* actually we do have an exception
* this works pretty nicely now
* up here
* this is better
* some minor clean up
* old function=now junk
* fix platform code test
* remove old platform code function
* now rename _new to
* use FW_QUERY_CONFIG
* clean up imports
* rename that too
* one line
* correct typing
correct typing
* draft tests
* so that works
* fixup excluded platform test now too
* this is tested by excluded platform test
* test parts and dates
* remove old comment
* old import
* take platform code stuff out of FwQueryConfig
* fix test
* revert debug script
* flip order
* make this a set by default
* revert this part
* correct typing
* clean up comments
* clean that test up too/pylint
* combine these three tests ina clean way
* not right
* more general
* be consistent with quotes
* comment
* comment
* comment in fw_versions
* flip order
* this is more readable
* could test all this, but it's tested in test_hyundai and doesn't do a lot here
* only assert brands which use this
* invalidate all CAN FD ICE and hybrid
* tuple
* can get away without filtering
* add comment reasons
* fix
* some review suggestions
* this works (first draft)
* this is better
* script to print platform codes and dates
* sanity check for dates are in correct ecus and platforms
* mypy
* better variable name and comment
* rename
* same
* slightly better name
* subset
* exclude platforms and live car without dates
* consistent
* self explan
* better name
* test to make sure the functions agree
* clean that up
* comment
* we get other responses from queries not in DB, only check any
* not used or typed
old-commit-hash: f788edb6a5c8d3f516076f886fe0d831ee43b580
											 
										 
										
											2 years ago 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      # If both have matches, they must agree 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  len ( matches )  ==  1  and  len ( brand_matches )  ==  1 : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        assert  matches  ==  brand_matches 
 
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												HKG: use platform codes to fuzzy fingerprint (#28531)
* get gas/ev/hev from FW (not all correct, poc)
* add test for essential ecus for fuzzy fingerprinting
* kinda works
* stash
* clean up
* add code
* simpler
* use the function
* test it with our cars
* no re
no re
no re
* debugging
* handle empty dict
* simpl
* this is promising
start on making existing fingerprinting functions use the config, instead of entirely replacing them
* needs to allow 1 match
* lay out how this should look
* changes
* executable
* some work
* use config
* fuzzy ecus
* config test
* comment and some clean up
* test platform codes
* use regex, simpler and fixes bug
* in func
* rm bad func
* typing for new func and remove old from dc
* todo done
* tested!
* remove fake platform codes
* thought we needed this, but actually...
* not needed
* not applicable any more
* use config for essential ecus
* first draft of test to make adding/removing fuzzy FP platform intentional
* compile
* clean up test
* even cleaner
* fix default ecus type
* temp fix
* this is mostly in tests now
* test every fuzzy ecu fw returns one platform code
* experiment with dates
* Revert "experiment with dates"
This reverts commit 3251b9cc5c3ca41ca92c8b75ad9b2234b720aa0b.
* clean that up
* comment
* test
* work on all cars
* fix fuzz_fw_fingerprint
* comment
* get first by search
* bit more clean up
* and more
* use compiled pattern for nicer syntax
* default
* flip dat around, much cleaner
* clean up hyundai test a bit
* flip order
same here
* rename test and flip subTest
* fix pylint
* revert fw changes
revert fw changes
* line
* add original functions to test
* needs to be a list
* cmt
* draft (need to count one ecu as a match)
* tiny clean up
* todo: date range
* only in notebook
* remove comment (still can be either list or set)
* same, only notebook
* more consistent signature
* copilot inspired
* copilot no good
* test for date parsing
* better name
* good, now we don't have to worry about the dates mismatching in another test/logic
* comment up+
* some stuff
* clean up
fix test
fix test
* test
* comment
* use utils
* clean up (utils are cleaner and less buggy)
* clean up (utils are cleaner and less buggy)
* fixup test
* use a dash (prettier) and remove some platforms that can fingerprint now!
* compile global pattern
* same as what we do in values
* remove comments
* fuzzy_get_platform_codes is one or none here
* more clean up
* sort imports
* woah woah woah
* add comment
* fix reassigning different types
* add types
* adapt fuzzy test recently added (nice it caught this!)
* update lock
* options
comments
* stash
* comments and fixes
* better comments
* better
* test: run on exact fuzzy matching logic, same results!
* use match_fw_to_car
* test all fw
* ex
* unused random
* this is a possibility
* this is more clear
* fix
* revert
* revert to needing both ECUs to match to reduce false positives, test
* fix excluded platform test :( but it's ok
* add comment
* we actually want to only test fuzzy ecus to make it explicit
* fix mypy
* comment for tomorrow
* just add matches with fuzzy FP
* add comment
* this was the cleanest I could think of, but still working on it. not very easy to understand
* think this is better, but also worse...
* comment: reframing how this works
* revert back to what we did before
* was swapped
* else set
* remove old comment
* fixes from merge
* remove fuzzy_min_match_count from this pr
* fix static analysis
* also unused
* different method first draft
* copy existing fuzzy func
* check all possible ecus exist, only platform codes, slightly refactor main loop
* fix
* Revert recent
Revert "fix"
This reverts commit 5cdb7bda835f1e48e750ab4195e891afe08e11ea.
Revert "check all possible ecus exist, only platform codes, slightly refactor main loop"
This reverts commit d3e918fa20fa4ce881445850f5f7428a3c11adf8.
Revert "copy existing fuzzy func"
This reverts commit 34c8c0545097c84f55f4b4f61907606c93760ddd.
Revert "different method first draft"
This reverts commit b91139055d7d1802c1eb726504798c156a183c9c.
* new func
* fixup test
* remove changes from v1 from fw_versions.py
* clean up a bit
* return part as part of code
* fix test
* add original fuzzy function
* add an ecu match if the date is within range (or date doesn't exist)
* add format for what we're going to do
* not working stash
* the exact matching function does more of what we want with less code and less custom logic
* we don't care about found versions, only codes and dates
* actually we do have an exception
* this works pretty nicely now
* up here
* this is better
* some minor clean up
* old function=now junk
* fix platform code test
* remove old platform code function
* now rename _new to
* use FW_QUERY_CONFIG
* clean up imports
* rename that too
* one line
* correct typing
correct typing
* draft tests
* so that works
* fixup excluded platform test now too
* this is tested by excluded platform test
* test parts and dates
* remove old comment
* old import
* take platform code stuff out of FwQueryConfig
* fix test
* revert debug script
* flip order
* make this a set by default
* revert this part
* correct typing
* clean up comments
* clean that test up too/pylint
* combine these three tests ina clean way
* not right
* more general
* be consistent with quotes
* comment
* comment
* comment in fw_versions
* flip order
* this is more readable
* could test all this, but it's tested in test_hyundai and doesn't do a lot here
* only assert brands which use this
* invalidate all CAN FD ICE and hybrid
* tuple
* can get away without filtering
* add comment reasons
* fix
* some review suggestions
* this works (first draft)
* this is better
* script to print platform codes and dates
* sanity check for dates are in correct ecus and platforms
* mypy
* better variable name and comment
* rename
* same
* slightly better name
* subset
* exclude platforms and live car without dates
* consistent
* self explan
* better name
* test to make sure the functions agree
* clean that up
* comment
* we get other responses from queries not in DB, only check any
* not used or typed
old-commit-hash: f788edb6a5c8d3f516076f886fe0d831ee43b580
											 
										 
										
											2 years ago 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  @parameterized . expand ( [ ( b ,  c ,  e [ c ] )  for  b ,  e  in  VERSIONS . items ( )  for  c  in  e ] ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_fuzzy_match_ecu_count ( self ,  brand ,  car_model ,  ecus ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # Asserts that fuzzy matching does not count matching FW, but ECU address keys 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    valid_ecus  =  [ e  for  e  in  ecus  if  e [ 0 ]  not  in  FUZZY_EXCLUDE_ECUS ] 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  not  len ( valid_ecus ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      pytest . skip ( " Car model has no compatible ECUs for fuzzy matching " ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    fw  =  [ ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  ecu  in  valid_ecus : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      ecu_name ,  addr ,  sub_addr  =  ecu 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  _  in  range ( 5 ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        # Add multiple FW versions to simulate ECU returning to multiple queries in a brand 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        fw . append ( { " ecu " :  ecu_name ,  " fwVersion " :  random . choice ( ecus [ ecu ] ) ,  ' brand ' :  brand , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								                   " address " :  addr ,  " subAddress " :  0  if  sub_addr  is  None  else  sub_addr } ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      CP  =  car . CarParams . new_message ( carFw = fw ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      _ ,  matches  =  match_fw_to_car ( CP . carFw ,  CP . carVin ,  allow_exact = False ,  log = False ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      # Assert no match if there are not enough unique ECUs 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      unique_ecus  =  { ( f [ ' address ' ] ,  f [ ' subAddress ' ] )  for  f  in  fw } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      if  len ( unique_ecus )  <  2 : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        assert  len ( matches )  ==  0 ,  car_model 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      # There won't always be a match due to shared FW, but if there is it should be correct 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      elif  len ( matches ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . assertFingerprints ( matches ,  car_model ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_fw_version_lists ( self ,  subtests ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    for  car_model ,  ecus  in  FW_VERSIONS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( car_model = car_model . value ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        for  ecu ,  ecu_fw  in  ecus . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          with  subtests . test ( ecu ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								            duplicates  =  { fw  for  fw  in  ecu_fw  if  ecu_fw . count ( fw )  >  1 } 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								            assert  not  len ( duplicates ) ,  f ' { car_model } : Duplicate FW versions: Ecu. { ECU_NAME [ ecu [ 0 ] ] } ,  { duplicates } ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            assert  len ( ecu_fw )  >  0 ,  f ' { car_model } : No FW versions: Ecu. { ECU_NAME [ ecu [ 0 ] ] } ' 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_all_addrs_map_to_one_ecu ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  brand ,  cars  in  VERSIONS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      addr_to_ecu  =  defaultdict ( set ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  ecus  in  cars . values ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  ecu_type ,  addr ,  sub_addr  in  ecus . keys ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          addr_to_ecu [ ( addr ,  sub_addr ) ] . add ( ecu_type ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          ecus_for_addr  =  addr_to_ecu [ ( addr ,  sub_addr ) ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          ecu_strings  =  " ,  " . join ( [ f ' Ecu. { ECU_NAME [ ecu ] } '  for  ecu  in  ecus_for_addr ] ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          assert  len ( ecus_for_addr )  < =  1 ,  f " { brand }  has multiple ECUs that map to one address:  { ecu_strings }  -> ( { hex ( addr ) } ,  { sub_addr } ) " 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_data_collection_ecus ( self ,  subtests ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    # Asserts no extra ECUs are in the fingerprinting database 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    for  brand ,  config  in  FW_QUERY_CONFIGS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  car_model ,  ecus  in  VERSIONS [ brand ] . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        bad_ecus  =  set ( ecus ) . intersection ( config . extra_ecus ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        with  subtests . test ( car_model = car_model . value ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          assert  not  len ( bad_ecus ) ,  f ' { car_model } : Fingerprints contain ECUs added for data collection:  { bad_ecus } ' 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_blacklisted_ecus ( self ,  subtests ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    blacklisted_addrs  =  ( 0x7c4 ,  0x7d0 )   # includes A/C ecu and an unknown ecu 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  car_model ,  ecus  in  FW_VERSIONS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( car_model = car_model . value ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        CP  =  interfaces [ car_model ] [ 0 ] . get_non_essential_params ( car_model ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        if  CP . carName  ==  ' subaru ' : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          for  ecu  in  ecus . keys ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								            assert  ecu [ 1 ]  not  in  blacklisted_addrs ,  f ' { car_model } : Blacklisted ecu: (Ecu. { ECU_NAME [ ecu [ 0 ] ] } ,  { hex ( ecu [ 1 ] ) } ) ' 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        elif  CP . carName  ==  " chrysler " : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          # Some HD trucks have a combined TCM and ECM 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          if  CP . carFingerprint . startswith ( " RAM HD " ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            for  ecu  in  ecus . keys ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								              assert  ecu [ 0 ]  !=  Ecu . transmission ,  f " { car_model } : Blacklisted ecu: (Ecu. { ECU_NAME [ ecu [ 0 ] ] } ,  { hex ( ecu [ 1 ] ) } ) " 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_non_essential_ecus ( self ,  subtests ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    for  brand ,  config  in  FW_QUERY_CONFIGS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( brand ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        # These ECUs are already not in ESSENTIAL_ECUS which the fingerprint functions give a pass if missing 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        unnecessary_non_essential_ecus  =  set ( config . non_essential_ecus )  -  set ( ESSENTIAL_ECUS ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        assert  unnecessary_non_essential_ecus  ==  set ( ) ,  " Declaring non-essential ECUs non-essential is not required:  "  +  \
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                                                                f " { ' ,  ' . join ( [ f ' Ecu. { ECU_NAME [ ecu ] } '  for  ecu  in  unnecessary_non_essential_ecus ] ) } " 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_missing_versions_and_configs ( self ,  subtests ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    brand_versions  =  set ( VERSIONS . keys ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    brand_configs  =  set ( FW_QUERY_CONFIGS . keys ( ) ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  len ( brand_configs  -  brand_versions ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        pytest . fail ( f " Brands do not implement FW_VERSIONS:  { brand_configs  -  brand_versions } " ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  len ( brand_versions  -  brand_configs ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        pytest . fail ( f " Brands do not implement FW_QUERY_CONFIG:  { brand_versions  -  brand_configs } " ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    # Ensure each brand has at least 1 ECU to query, and extra ECU retrieval 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  brand ,  config  in  FW_QUERY_CONFIGS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      assert  len ( config . get_all_ecus ( { } ,  include_extra_ecus = False ) )  ==  0 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      assert  config . get_all_ecus ( { } )  ==  set ( config . extra_ecus ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      assert  len ( config . get_all_ecus ( VERSIONS [ brand ] ) )  >  0 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_fw_request_ecu_whitelist ( self ,  subtests ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    for  brand ,  config  in  FW_QUERY_CONFIGS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( brand = brand ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        whitelisted_ecus  =  { ecu  for  r  in  config . requests  for  ecu  in  r . whitelist_ecus } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        brand_ecus  =  { fw [ 0 ]  for  car_fw  in  VERSIONS [ brand ] . values ( )  for  fw  in  car_fw } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        brand_ecus  | =  { ecu [ 0 ]  for  ecu  in  config . extra_ecus } 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        # each ecu in brand's fw versions + extra ecus needs to be whitelisted at least once 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ecus_not_whitelisted  =  brand_ecus  -  whitelisted_ecus 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ecu_strings  =  " ,  " . join ( [ f ' Ecu. { ECU_NAME [ ecu ] } '  for  ecu  in  ecus_not_whitelisted ] ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        assert  not  ( len ( whitelisted_ecus )  and  len ( ecus_not_whitelisted ) ) ,  \
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                         f ' { brand . title ( ) } : ECUs not in any FW query whitelists:  { ecu_strings } ' 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_fw_requests ( self ,  subtests ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    # Asserts equal length request and response lists 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  brand ,  config  in  FW_QUERY_CONFIGS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( brand = brand ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        for  request_obj  in  config . requests : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          assert  len ( request_obj . request )  ==  len ( request_obj . response ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          # No request on the OBD port (bus 1, multiplexed) should be run on an aux panda 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          assert  not  ( request_obj . auxiliary  and  request_obj . bus  ==  1  and  request_obj . obd_multiplexing ) ,  \
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								                           f " { brand . title ( ) } : OBD multiplexed request is marked auxiliary:  { request_obj } " 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_brand_ecu_matches ( self ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    empty_response  =  { brand :  set ( )  for  brand  in  FW_QUERY_CONFIGS } 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    assert  get_brand_ecu_matches ( set ( ) )  ==  empty_response 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    # we ignore bus 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    expected_response  =  empty_response  |  { ' toyota ' :  { ( 0x750 ,  0xf ) } } 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    assert  get_brand_ecu_matches ( { ( 0x758 ,  0xf ,  99 ) } )  ==  expected_response 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								class  TestFwFingerprintTiming : 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  N :  int  =  5 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  TOL :  float  =  0.05 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  # for patched functions 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  current_obd_multiplexing :  bool 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  total_time :  float 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  fake_set_obd_multiplexing ( self ,  _ ,  obd_multiplexing ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    """ The 10Hz blocking params loop adds on average 50ms to the query time for each OBD multiplexing change """ 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  obd_multiplexing  !=  self . current_obd_multiplexing : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . current_obd_multiplexing  =  obd_multiplexing 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . total_time  + =  0.1  /  2 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								  def  fake_get_data ( self ,  timeout ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    self . total_time  + =  timeout 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  { } 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  _benchmark_brand ( self ,  brand ,  num_pandas ,  mocker ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    fake_socket  =  FakeSocket ( ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    self . total_time  =  0 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    mocker . patch ( " openpilot.selfdrive.car.fw_versions.set_obd_multiplexing " ,  self . fake_set_obd_multiplexing ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mocker . patch ( " openpilot.selfdrive.car.isotp_parallel_query.IsoTpParallelQuery.get_data " ,  self . fake_get_data ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  _  in  range ( self . N ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      # Treat each brand as the most likely (aka, the first) brand with OBD multiplexing initially on 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . current_obd_multiplexing  =  True 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      t  =  time . perf_counter ( ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      get_fw_versions ( fake_socket ,  fake_socket ,  brand ,  num_pandas = num_pandas ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . total_time  + =  time . perf_counter ( )  -  t 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  self . total_time  /  self . N 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  _assert_timing ( self ,  avg_time ,  ref_time ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    assert  avg_time  <  ref_time  +  self . TOL 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    assert  avg_time  >  ref_time  -  self . TOL ,  " Performance seems to have improved, update test refs. " 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_startup_timing ( self ,  subtests ,  mocker ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    # Tests worse-case VIN query time and typical present ECU query time 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    vin_ref_times  =  { ' worst ' :  1.4 ,  ' best ' :  0.7 }   # best assumes we go through all queries to get a match 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    present_ecu_ref_time  =  0.45 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    def  fake_get_ecu_addrs ( * _ ,  timeout ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . total_time  + =  timeout 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      return  set ( ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    fake_socket  =  FakeSocket ( ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    self . total_time  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    mocker . patch ( " openpilot.selfdrive.car.fw_versions.set_obd_multiplexing " ,  self . fake_set_obd_multiplexing ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    mocker . patch ( " openpilot.selfdrive.car.fw_versions.get_ecu_addrs " ,  fake_get_ecu_addrs ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  _  in  range ( self . N ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      self . current_obd_multiplexing  =  True 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      get_present_ecus ( fake_socket ,  fake_socket ,  num_pandas = 2 ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    self . _assert_timing ( self . total_time  /  self . N ,  present_ecu_ref_time ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    print ( f ' get_present_ecus, query time= { self . total_time  /  self . N }  seconds ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    for  name ,  args  in  ( ( ' worst ' ,  { } ) ,  ( ' best ' ,  { ' retry ' :  1 } ) ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( name = name ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        self . total_time  =  0.0 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        mocker . patch ( " openpilot.selfdrive.car.isotp_parallel_query.IsoTpParallelQuery.get_data " ,  self . fake_get_data ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  _  in  range ( self . N ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          get_vin ( fake_socket ,  fake_socket ,  ( 0 ,  1 ) ,  * * args ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        self . _assert_timing ( self . total_time  /  self . N ,  vin_ref_times [ name ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        print ( f ' get_vin  { name }  case, query time= { self . total_time  /  self . N }  seconds ' ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								  def  test_fw_query_timing ( self ,  subtests ,  mocker ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    total_ref_time  =  { 1 :  7.2 ,  2 :  7.8 } 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    brand_ref_times  =  { 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      1 :  { 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' gm ' :  1.0 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' body ' :  0.1 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' chrysler ' :  0.3 , 
 
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												Ford: log interesting module configuration data (#31569)
* Ford: log interesting module configuration data
Ford ECUs have what is called "As-Built Data" which is configured at the
factory/workshop to set what packages/features are enabled on the car.
But they also contain vehicle specific information (VIN, make, model,
weight, wheel base...), DTC information and driver preferences.
I dumped the CAN traffic for the FORScan diagnostic tool to see how it
requests this information from the ECUs.
<details>
<summary>FORScan communication with IPMA (camera)</summary>
<pre>
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': '0200'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'TESTER_PRESENT', 'hex': '00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'TESTER_PRESENT', 'hex': '00'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f190'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f113'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x13JX7T-19H406-CH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1134a5837542d3139483430362d434800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f188'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x88JX7T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1884a5837542d3134463339372d414800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f120'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1 JX7T-14F397-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1204a5837542d3134463339372d424600000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f121'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f124'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1$JX7T-14F398-AG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1244a5837542d3134463339382d414700000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f125'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1%JX7T-14F398-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1254a5837542d3134463339382d424600000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f126'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f10a'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f111'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x11JX7T-14F403-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1114a5837542d3134463430332d434100000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f18c'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x8c182762191\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f18c31383237363231393100000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f162'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f110'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x10DS-JX7T-19H406-AD\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f11044532d4a5837542d3139483430362d414400000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': '0202'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd100'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd10001'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd700'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd70001010101'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd701'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd70101020000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'dd01'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'dd010102f8'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f113'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x13JX7T-19H406-CH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1134a5837542d3139483430362d434800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd08'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd0800000500500100300000000000000000000000300000000000000000200100400300100200001200f00300500000000000000300c00b00400200000000000000000000000000000000200f01201e01501400a00200200400700d02501d01700700e06405005e05503401100a000000002002002001000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd09'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd09ffec0001fef60002'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DTC_INFORMATION', 'hex': '028f'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DTC_INFORMATION', 'hex': '02ff50019768c253002cc401862cc418862c'}
... skip DTC requests ...
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de01'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de01fd5616db5fffff557fe1f842080000000800000008000000080000000819bfe00f7c00000000000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de02'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de02800000008000000080000000800000008337fc00800000008000000080000000800000008337fc0000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de03'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de03fffc26c3800000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de04'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de04546a8c0000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de05'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de06'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de07'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de08'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de09'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0a'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0b'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0c'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0d'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0e'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
</pre>
</details>
Using UDS service `READ_DATA_BY_IDENTIFIER`, we can read the As-Built
blocks from `0xDExx` with no diagnostic session/security access necessary.
I used [Online As-Built databases](https://cyanlabs.net/asbuilt-db/) and
various coding spreadsheets shared online to find values we might be
interested in using for fingerprinting (both vehicle parameters and
identifying the platform).
ABS:
- Payload tier (Base, Mid Payload Upgrade, Heavy Duty Payload
  Upgrade...)
- Wheelbase
- Steering ratio
- Cruise Control Mode (Normal, Adaptive)
- Enable Stop and Go
PSCM:
- Enable Lane Keeping Aid
- Enable Traffic Jam Assist
- Enable Lane Centering Assist
IPMA (Q4):
- Steering ratio
- Wheelbase
APIM (Sync 3 and Sync 4):
- Steering ratio
- Vehicle weight
- Wheelbase
There are more potentially useful signals which I haven't included
although they might not be necessary:
- Vehicle (Ford platform code, like "C344" or "C519" - although the
  source of the mapping from index to code is FORScan and not Ford
themselves unless we can find a better source).
- Fuel type
- Vehicle length/height/front track/rear track
- Tire circumference (could be useful for converting wheel speed rad/s
  to m/s)
- Steering angle source (Pinion, Wheel)
- Country code (letters, e.g. US, CA or UK)
- Transmission type
- CAN network architecture
- More feature flags (the APIM also stores settings for ACC, LCA, BLIS)
The full list of settings I have found is
[here](https://github.com/incognitojam/op-notebooks/blob/main/ford/settings.py).
* FwQueryConfig: add data_requests
* add car_data to CarInterface get_params
* Revert "add car_data to CarInterface get_params"
This reverts commit aa161a6b82082705db97bea2c4317e1888a74845.
* test_ford: add APIM ecu address
* Revert "FwQueryConfig: add data_requests"
This reverts commit dc5484a9b80be5bc61a7fdf55560b8813bc43ef7.
* fix block numbers and add extra queries
* bump test_fw_query_timing
* add missing query whitelists
* simplify asbuilt requests
* use forscan block ids
* formatting
---------
Co-authored-by: Shane Smiskol <shane@smiskol.com>
old-commit-hash: c724d1c86ce4044bfc03f1e7941871466f51a708
											 
										 
										
											2 years ago 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' ford ' :  1.5 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' honda ' :  0.45 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' hyundai ' :  0.65 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' mazda ' :  0.1 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' nissan ' :  0.8 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' subaru ' :  0.65 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' tesla ' :  0.3 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' toyota ' :  0.7 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' volkswagen ' :  0.65 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      } , 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      2 :  { 
 
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												Ford: log interesting module configuration data (#31569)
* Ford: log interesting module configuration data
Ford ECUs have what is called "As-Built Data" which is configured at the
factory/workshop to set what packages/features are enabled on the car.
But they also contain vehicle specific information (VIN, make, model,
weight, wheel base...), DTC information and driver preferences.
I dumped the CAN traffic for the FORScan diagnostic tool to see how it
requests this information from the ECUs.
<details>
<summary>FORScan communication with IPMA (camera)</summary>
<pre>
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': '0200'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'TESTER_PRESENT', 'hex': '00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'TESTER_PRESENT', 'hex': '00'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f190'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f113'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x13JX7T-19H406-CH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1134a5837542d3139483430362d434800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f188'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x88JX7T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1884a5837542d3134463339372d414800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f120'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1 JX7T-14F397-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1204a5837542d3134463339372d424600000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f121'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f124'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1$JX7T-14F398-AG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1244a5837542d3134463339382d414700000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f125'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1%JX7T-14F398-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1254a5837542d3134463339382d424600000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f126'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f10a'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f111'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x11JX7T-14F403-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1114a5837542d3134463430332d434100000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f18c'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x8c182762191\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f18c31383237363231393100000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f162'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f110'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x10DS-JX7T-19H406-AD\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f11044532d4a5837542d3139483430362d414400000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': '0202'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd100'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd10001'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd700'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd70001010101'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd701'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd70101020000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'dd01'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'dd010102f8'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f113'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x13JX7T-19H406-CH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1134a5837542d3139483430362d434800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd08'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd0800000500500100300000000000000000000000300000000000000000200100400300100200001200f00300500000000000000300c00b00400200000000000000000000000000000000200f01201e01501400a00200200400700d02501d01700700e06405005e05503401100a000000002002002001000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd09'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd09ffec0001fef60002'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DTC_INFORMATION', 'hex': '028f'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DTC_INFORMATION', 'hex': '02ff50019768c253002cc401862cc418862c'}
... skip DTC requests ...
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de01'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de01fd5616db5fffff557fe1f842080000000800000008000000080000000819bfe00f7c00000000000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de02'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de02800000008000000080000000800000008337fc00800000008000000080000000800000008337fc0000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de03'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de03fffc26c3800000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de04'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de04546a8c0000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de05'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de06'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de07'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de08'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de09'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0a'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0b'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0c'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0d'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0e'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
</pre>
</details>
Using UDS service `READ_DATA_BY_IDENTIFIER`, we can read the As-Built
blocks from `0xDExx` with no diagnostic session/security access necessary.
I used [Online As-Built databases](https://cyanlabs.net/asbuilt-db/) and
various coding spreadsheets shared online to find values we might be
interested in using for fingerprinting (both vehicle parameters and
identifying the platform).
ABS:
- Payload tier (Base, Mid Payload Upgrade, Heavy Duty Payload
  Upgrade...)
- Wheelbase
- Steering ratio
- Cruise Control Mode (Normal, Adaptive)
- Enable Stop and Go
PSCM:
- Enable Lane Keeping Aid
- Enable Traffic Jam Assist
- Enable Lane Centering Assist
IPMA (Q4):
- Steering ratio
- Wheelbase
APIM (Sync 3 and Sync 4):
- Steering ratio
- Vehicle weight
- Wheelbase
There are more potentially useful signals which I haven't included
although they might not be necessary:
- Vehicle (Ford platform code, like "C344" or "C519" - although the
  source of the mapping from index to code is FORScan and not Ford
themselves unless we can find a better source).
- Fuel type
- Vehicle length/height/front track/rear track
- Tire circumference (could be useful for converting wheel speed rad/s
  to m/s)
- Steering angle source (Pinion, Wheel)
- Country code (letters, e.g. US, CA or UK)
- Transmission type
- CAN network architecture
- More feature flags (the APIM also stores settings for ACC, LCA, BLIS)
The full list of settings I have found is
[here](https://github.com/incognitojam/op-notebooks/blob/main/ford/settings.py).
* FwQueryConfig: add data_requests
* add car_data to CarInterface get_params
* Revert "add car_data to CarInterface get_params"
This reverts commit aa161a6b82082705db97bea2c4317e1888a74845.
* test_ford: add APIM ecu address
* Revert "FwQueryConfig: add data_requests"
This reverts commit dc5484a9b80be5bc61a7fdf55560b8813bc43ef7.
* fix block numbers and add extra queries
* bump test_fw_query_timing
* add missing query whitelists
* simplify asbuilt requests
* use forscan block ids
* formatting
---------
Co-authored-by: Shane Smiskol <shane@smiskol.com>
old-commit-hash: c724d1c86ce4044bfc03f1e7941871466f51a708
											 
										 
										
											2 years ago 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' ford ' :  1.6 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' hyundai ' :  1.15 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        ' tesla ' :  0.3 , 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    total_times  =  { 1 :  0.0 ,  2 :  0.0 } 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    for  num_pandas  in  ( 1 ,  2 ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								      for  brand ,  config  in  FW_QUERY_CONFIGS . items ( ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        with  subtests . test ( brand = brand ,  num_pandas = num_pandas ) : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          avg_time  =  self . _benchmark_brand ( brand ,  num_pandas ,  mocker ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          total_times [ num_pandas ]  + =  avg_time 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          avg_time  =  round ( avg_time ,  2 ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          ref_time  =  brand_ref_times [ num_pandas ] . get ( brand ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          if  ref_time  is  None : 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            # ref time should be same as 1 panda if no aux queries 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            ref_time  =  brand_ref_times [ num_pandas  -  1 ] [ brand ] 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								          self . _assert_timing ( avg_time ,  ref_time ) 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								          print ( f ' { brand =} ,  { num_pandas =} ,  { len ( config . requests ) =} , avg FW query time= { avg_time }  seconds ' ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    for  num_pandas  in  ( 1 ,  2 ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								      with  subtests . test ( brand = ' all_brands ' ,  num_pandas = num_pandas ) : 
 
							 
						 
					
						
							
								
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        total_time  =  round ( total_times [ num_pandas ] ,  2 ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        self . _assert_timing ( total_time ,  total_ref_time [ num_pandas ] ) 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        print ( f ' all brands, total FW query time= { total_time }  seconds ' )