hitl_tester.test_cases.bms.firmware_flash_save
| Test | Save flash when reprogrammed |
|---|---|
| GitHub Issue(s) | turnaroundfactor/HITL#327 turnaroundfactor/HITL#342 |
| Description | Don't overwrite calibration/faults when reprogramming |
1""" 2| Test | Save flash when reprogrammed | 3| :------------------- | :----------------------------------------------------------- | 4| GitHub Issue(s) | turnaroundfactor/HITL#327 </br>\ 5 turnaroundfactor/HITL#342 | 6| Description | Don't overwrite calibration/faults when reprogramming | 7""" 8 9from __future__ import annotations 10 11import ctypes 12import time 13 14import pytest 15 16from hitl_tester.modules.bms.adc_plate import ADCPlate 17from hitl_tester.modules.bms.bms_hw import BMSHardware 18from hitl_tester.modules.bms.bms_serial import serial_monitor 19from hitl_tester.modules.bms.plateset import Plateset 20from hitl_tester.modules.bms.smbus import SMBus 21from hitl_tester.modules.bms.smbus_types import SMBusReg, BMSCommands 22from hitl_tester.modules.logger import logger 23from hitl_tester.test_cases.bms.test_flash_firmware import test_flash_firmware 24 25FLASH_SLEEP = 7 26"""Time to wait for flash to write.""" 27 28_bms = BMSHardware(pytest.flags) # type: ignore[arg-type] 29_bms.init() 30_plateset = Plateset() 31_adc_plate = ADCPlate() 32_smbus = SMBus() 33 34 35def standard_rest(seconds: float = 2 * 3600, sample_interval: int = 10): 36 """Helper function to stabilize the batteries for 2+ hours.""" 37 _bms.max_time = seconds 38 _bms.sample_interval = sample_interval 39 _bms.run_resting_cycle() 40 41 42@pytest.mark.sim_cells 43class TestFlashPreservation: 44 """Confirm flash is preserved.""" 45 46 average = 0 47 readings = 0 48 49 def bms_current(self): 50 """Measure serial current and calculate an average.""" 51 new_reading = _bms.csv.cycle.last_serial_data["mamps"] 52 self.average = (new_reading + self.readings * self.average) / (self.readings + 1) 53 self.readings += 1 54 logger.write_info_to_report(f"BMS Serial Current (mA): {new_reading:.3f}") # Output current on every sample 55 56 def calibrate_current(self): 57 """Calibrate BMS current in flash.""" 58 _bms.csv.cycle.postfix_fn = self.bms_current # Get current on each sample 59 self.readings = 0 60 standard_rest(30, 5) 61 logger.write_result_to_html_report(f"Average rest current: {self.average:.3f} mA") 62 63 # Calibrate 64 offset = int(round(self.average, 0)) 65 logger.write_info_to_report(f"Setting offset current to {offset:.3f} mA") 66 data = (ctypes.c_uint8(offset).value << 8) | BMSCommands.CALIBRATE 67 _smbus.write_register(SMBusReg.MANUFACTURING_ACCESS, data) 68 time.sleep(FLASH_SLEEP) 69 data = _smbus.read_register(SMBusReg.MANUFACTURING_ACCESS)[0] 70 logger.write_info_to_report(f"{SMBusReg.MANUFACTURING_ACCESS.fname}: {data:04X}") 71 72 _bms.csv.cycle.postfix_fn = lambda: ... 73 74 def is_calibrated(self) -> bool: 75 """Confirm current is in acceptable range""" 76 acceptable_error_ma = 5 77 78 _bms.csv.cycle.postfix_fn = self.bms_current # Get current on each sample 79 self.readings = 0 # Reset average 80 standard_rest(30, 5) 81 logger.write_result_to_html_report(f"Average rest current (calibrated?): {self.average:.3f} mA") 82 return acceptable_error_ma > self.average > -acceptable_error_ma 83 84 def enable_faults(self): 85 """Enable faults via SMBus.""" 86 _smbus.write_register(SMBusReg.MANUFACTURING_ACCESS, BMSCommands.FAULT_ENABLE) 87 time.sleep(FLASH_SLEEP) 88 89 def is_faults_enabled(self) -> bool: 90 """Check if overtemp faults can be raised.""" 91 timeout_s = 10 92 93 # Raise a fault 94 _plateset.thermistor1 = 65 95 start = time.perf_counter() 96 while (serial_data := serial_monitor.read(latest=True)) and not serial_data["flags.fault_overtemp_discharge"]: 97 if time.perf_counter() - start > timeout_s: 98 logger.write_debug_to_report(f"Over-temperature fault was not raised after {timeout_s} seconds.") 99 return False 100 101 # Clear the fault 102 _plateset.thermistor1 = 45 103 start = time.perf_counter() 104 while (serial_data := serial_monitor.read(latest=True)) and serial_data["flags.fault_overtemp_discharge"]: 105 if time.perf_counter() - start > timeout_s: 106 logger.write_debug_to_report(f"Over-temperature fault was not cleared after {timeout_s} seconds.") 107 return False 108 109 return True 110 111 def test_flash_preservation(self): 112 """ 113 | Description | Confirm calibration/faults are preserved when reprogrammed | 114 | :------------------- | :--------------------------------------------------------------------- | 115 | GitHub Issue | turnaroundfactor/HITL#347 | 116 | Instructions | 1. Set up flash values </br>\ 117 ⠀⠀⦁ Calibrate the BMS and verify </br>\ 118 ⠀⠀⦁ Enable the faults and verify </br>\ 119 2. Flash new firmware </br>\ 120 3. Confirm BMS calibration/faults are not erased </br>\ 121 ⠀⠀⦁ BMS is calibrated </br>\ 122 ⠀⠀⦁ Faults are enabled | 123 | Pass / Fail Criteria | Pass if flash is preserved | 124 | Estimated Duration | 1 minute | 125 """ 126 127 # 1. Set up flash values, confirm they're set 128 self.calibrate_current() 129 self.enable_faults() 130 assert self.is_calibrated(), "Current was not calibrated." 131 assert self.is_faults_enabled(), "Faults were not enabled." 132 133 # 2/3. Erase flash, confirm BMS flash is erased 134 test_flash_firmware() 135 logger.write_info_to_report("Sleeping for 10 seconds before starting...") 136 time.sleep(10) 137 assert self.is_calibrated(), "Current was not calibrated after firmware flash." 138 assert self.is_faults_enabled(), "Faults were not enabled after firmware flash."
FLASH_SLEEP =
7
Time to wait for flash to write.
def
standard_rest(seconds: float = 7200, sample_interval: int = 10):
36def standard_rest(seconds: float = 2 * 3600, sample_interval: int = 10): 37 """Helper function to stabilize the batteries for 2+ hours.""" 38 _bms.max_time = seconds 39 _bms.sample_interval = sample_interval 40 _bms.run_resting_cycle()
Helper function to stabilize the batteries for 2+ hours.
@pytest.mark.sim_cells
class
TestFlashPreservation:
43@pytest.mark.sim_cells 44class TestFlashPreservation: 45 """Confirm flash is preserved.""" 46 47 average = 0 48 readings = 0 49 50 def bms_current(self): 51 """Measure serial current and calculate an average.""" 52 new_reading = _bms.csv.cycle.last_serial_data["mamps"] 53 self.average = (new_reading + self.readings * self.average) / (self.readings + 1) 54 self.readings += 1 55 logger.write_info_to_report(f"BMS Serial Current (mA): {new_reading:.3f}") # Output current on every sample 56 57 def calibrate_current(self): 58 """Calibrate BMS current in flash.""" 59 _bms.csv.cycle.postfix_fn = self.bms_current # Get current on each sample 60 self.readings = 0 61 standard_rest(30, 5) 62 logger.write_result_to_html_report(f"Average rest current: {self.average:.3f} mA") 63 64 # Calibrate 65 offset = int(round(self.average, 0)) 66 logger.write_info_to_report(f"Setting offset current to {offset:.3f} mA") 67 data = (ctypes.c_uint8(offset).value << 8) | BMSCommands.CALIBRATE 68 _smbus.write_register(SMBusReg.MANUFACTURING_ACCESS, data) 69 time.sleep(FLASH_SLEEP) 70 data = _smbus.read_register(SMBusReg.MANUFACTURING_ACCESS)[0] 71 logger.write_info_to_report(f"{SMBusReg.MANUFACTURING_ACCESS.fname}: {data:04X}") 72 73 _bms.csv.cycle.postfix_fn = lambda: ... 74 75 def is_calibrated(self) -> bool: 76 """Confirm current is in acceptable range""" 77 acceptable_error_ma = 5 78 79 _bms.csv.cycle.postfix_fn = self.bms_current # Get current on each sample 80 self.readings = 0 # Reset average 81 standard_rest(30, 5) 82 logger.write_result_to_html_report(f"Average rest current (calibrated?): {self.average:.3f} mA") 83 return acceptable_error_ma > self.average > -acceptable_error_ma 84 85 def enable_faults(self): 86 """Enable faults via SMBus.""" 87 _smbus.write_register(SMBusReg.MANUFACTURING_ACCESS, BMSCommands.FAULT_ENABLE) 88 time.sleep(FLASH_SLEEP) 89 90 def is_faults_enabled(self) -> bool: 91 """Check if overtemp faults can be raised.""" 92 timeout_s = 10 93 94 # Raise a fault 95 _plateset.thermistor1 = 65 96 start = time.perf_counter() 97 while (serial_data := serial_monitor.read(latest=True)) and not serial_data["flags.fault_overtemp_discharge"]: 98 if time.perf_counter() - start > timeout_s: 99 logger.write_debug_to_report(f"Over-temperature fault was not raised after {timeout_s} seconds.") 100 return False 101 102 # Clear the fault 103 _plateset.thermistor1 = 45 104 start = time.perf_counter() 105 while (serial_data := serial_monitor.read(latest=True)) and serial_data["flags.fault_overtemp_discharge"]: 106 if time.perf_counter() - start > timeout_s: 107 logger.write_debug_to_report(f"Over-temperature fault was not cleared after {timeout_s} seconds.") 108 return False 109 110 return True 111 112 def test_flash_preservation(self): 113 """ 114 | Description | Confirm calibration/faults are preserved when reprogrammed | 115 | :------------------- | :--------------------------------------------------------------------- | 116 | GitHub Issue | turnaroundfactor/HITL#347 | 117 | Instructions | 1. Set up flash values </br>\ 118 ⠀⠀⦁ Calibrate the BMS and verify </br>\ 119 ⠀⠀⦁ Enable the faults and verify </br>\ 120 2. Flash new firmware </br>\ 121 3. Confirm BMS calibration/faults are not erased </br>\ 122 ⠀⠀⦁ BMS is calibrated </br>\ 123 ⠀⠀⦁ Faults are enabled | 124 | Pass / Fail Criteria | Pass if flash is preserved | 125 | Estimated Duration | 1 minute | 126 """ 127 128 # 1. Set up flash values, confirm they're set 129 self.calibrate_current() 130 self.enable_faults() 131 assert self.is_calibrated(), "Current was not calibrated." 132 assert self.is_faults_enabled(), "Faults were not enabled." 133 134 # 2/3. Erase flash, confirm BMS flash is erased 135 test_flash_firmware() 136 logger.write_info_to_report("Sleeping for 10 seconds before starting...") 137 time.sleep(10) 138 assert self.is_calibrated(), "Current was not calibrated after firmware flash." 139 assert self.is_faults_enabled(), "Faults were not enabled after firmware flash."
Confirm flash is preserved.
def
bms_current(self):
50 def bms_current(self): 51 """Measure serial current and calculate an average.""" 52 new_reading = _bms.csv.cycle.last_serial_data["mamps"] 53 self.average = (new_reading + self.readings * self.average) / (self.readings + 1) 54 self.readings += 1 55 logger.write_info_to_report(f"BMS Serial Current (mA): {new_reading:.3f}") # Output current on every sample
Measure serial current and calculate an average.
def
calibrate_current(self):
57 def calibrate_current(self): 58 """Calibrate BMS current in flash.""" 59 _bms.csv.cycle.postfix_fn = self.bms_current # Get current on each sample 60 self.readings = 0 61 standard_rest(30, 5) 62 logger.write_result_to_html_report(f"Average rest current: {self.average:.3f} mA") 63 64 # Calibrate 65 offset = int(round(self.average, 0)) 66 logger.write_info_to_report(f"Setting offset current to {offset:.3f} mA") 67 data = (ctypes.c_uint8(offset).value << 8) | BMSCommands.CALIBRATE 68 _smbus.write_register(SMBusReg.MANUFACTURING_ACCESS, data) 69 time.sleep(FLASH_SLEEP) 70 data = _smbus.read_register(SMBusReg.MANUFACTURING_ACCESS)[0] 71 logger.write_info_to_report(f"{SMBusReg.MANUFACTURING_ACCESS.fname}: {data:04X}") 72 73 _bms.csv.cycle.postfix_fn = lambda: ...
Calibrate BMS current in flash.
def
is_calibrated(self) -> bool:
75 def is_calibrated(self) -> bool: 76 """Confirm current is in acceptable range""" 77 acceptable_error_ma = 5 78 79 _bms.csv.cycle.postfix_fn = self.bms_current # Get current on each sample 80 self.readings = 0 # Reset average 81 standard_rest(30, 5) 82 logger.write_result_to_html_report(f"Average rest current (calibrated?): {self.average:.3f} mA") 83 return acceptable_error_ma > self.average > -acceptable_error_ma
Confirm current is in acceptable range
def
enable_faults(self):
85 def enable_faults(self): 86 """Enable faults via SMBus.""" 87 _smbus.write_register(SMBusReg.MANUFACTURING_ACCESS, BMSCommands.FAULT_ENABLE) 88 time.sleep(FLASH_SLEEP)
Enable faults via SMBus.
def
is_faults_enabled(self) -> bool:
90 def is_faults_enabled(self) -> bool: 91 """Check if overtemp faults can be raised.""" 92 timeout_s = 10 93 94 # Raise a fault 95 _plateset.thermistor1 = 65 96 start = time.perf_counter() 97 while (serial_data := serial_monitor.read(latest=True)) and not serial_data["flags.fault_overtemp_discharge"]: 98 if time.perf_counter() - start > timeout_s: 99 logger.write_debug_to_report(f"Over-temperature fault was not raised after {timeout_s} seconds.") 100 return False 101 102 # Clear the fault 103 _plateset.thermistor1 = 45 104 start = time.perf_counter() 105 while (serial_data := serial_monitor.read(latest=True)) and serial_data["flags.fault_overtemp_discharge"]: 106 if time.perf_counter() - start > timeout_s: 107 logger.write_debug_to_report(f"Over-temperature fault was not cleared after {timeout_s} seconds.") 108 return False 109 110 return True
Check if overtemp faults can be raised.
def
test_flash_preservation(self):
112 def test_flash_preservation(self): 113 """ 114 | Description | Confirm calibration/faults are preserved when reprogrammed | 115 | :------------------- | :--------------------------------------------------------------------- | 116 | GitHub Issue | turnaroundfactor/HITL#347 | 117 | Instructions | 1. Set up flash values </br>\ 118 ⠀⠀⦁ Calibrate the BMS and verify </br>\ 119 ⠀⠀⦁ Enable the faults and verify </br>\ 120 2. Flash new firmware </br>\ 121 3. Confirm BMS calibration/faults are not erased </br>\ 122 ⠀⠀⦁ BMS is calibrated </br>\ 123 ⠀⠀⦁ Faults are enabled | 124 | Pass / Fail Criteria | Pass if flash is preserved | 125 | Estimated Duration | 1 minute | 126 """ 127 128 # 1. Set up flash values, confirm they're set 129 self.calibrate_current() 130 self.enable_faults() 131 assert self.is_calibrated(), "Current was not calibrated." 132 assert self.is_faults_enabled(), "Faults were not enabled." 133 134 # 2/3. Erase flash, confirm BMS flash is erased 135 test_flash_firmware() 136 logger.write_info_to_report("Sleeping for 10 seconds before starting...") 137 time.sleep(10) 138 assert self.is_calibrated(), "Current was not calibrated after firmware flash." 139 assert self.is_faults_enabled(), "Faults were not enabled after firmware flash."
| Description | Confirm calibration/faults are preserved when reprogrammed |
|---|---|
| GitHub Issue | turnaroundfactor/HITL#347 |
| Instructions | 1. Set up flash values ⠀⠀⦁ Calibrate the BMS and verify ⠀⠀⦁ Enable the faults and verify 2. Flash new firmware 3. Confirm BMS calibration/faults are not erased ⠀⠀⦁ BMS is calibrated ⠀⠀⦁ Faults are enabled |
| Pass / Fail Criteria | Pass if flash is preserved |
| Estimated Duration | 1 minute |