hitl_tester.test_cases.bms.test_arts
Modified version of the tests in test_2590_cell_sims.py, for testing 17S and 20S arts energy packs.
Used in these test plans:
- arts_20s_hitl_1_4 ⠀⠀⠀(bms/arts_20s_hitl_1_4.plan)
- arts_17s_hitl_1_3 ⠀⠀⠀(bms/arts_17s_hitl_1_3.plan)
Example Command (warning: test plan may run other test cases):
./hitl_tester.py arts_20s_hitl_1_4 -DTYPE=17s -DCHARGE_VOLTAGE=28.9 -DCHARGE_OVERVOLTAGE=28.9 -DSAMPLE_INTERVAL=10 -DSTEP_START=1 -DSTEP_END=5 -DRECONDITION=False
1""" 2Modified version of the tests in test_2590_cell_sims.py, for testing 17S and 20S arts energy packs. 3""" 4 5from __future__ import annotations 6 7from contextlib import suppress 8 9import pytest 10 11from hitl_tester.modules.bms.bms_hw import BMSHardware 12from hitl_tester.modules.bms.plateset import Plateset 13from hitl_tester.modules.bms_types import DischargeType, TimeoutExceededError 14from hitl_tester.modules.logger import logger 15 16TYPE = "17s" # 20s or 17s 17CHARGE_VOLTAGE = 28.9 if TYPE == "17s" else 35 18CHARGE_OVERVOLTAGE = 28.9 if TYPE == "17s" else 35 # 35V for the 20S and 28.9V for the 17S 19SAMPLE_INTERVAL = 10 20STEP_START = 1 21STEP_END = 5 22RECONDITION = False 23 24bms_hardware = BMSHardware(pytest.flags) # type: ignore[arg-type] 25bms_hardware.init() 26 27plateset = Plateset() 28 29test_step = pytest.mark.skipif(RECONDITION, reason="Running recondition cycles (skipping tests)") 30recondition_step = pytest.mark.skipif(not RECONDITION, reason="Running test cycles (skipping recondition)") 31 32 33def standard_charge( 34 charge_current: float = 0.800, 35 max_time: int = 16 * 3600, 36 sample_interval: int = SAMPLE_INTERVAL, 37 minimum_readings: int = 3, 38 charge_voltage: float = CHARGE_VOLTAGE, 39 termination_current: float = -0.05, # Wait for timeout instead 40): 41 """Charge cells / pack.""" 42 bms_hardware.voltage = charge_voltage 43 bms_hardware.ov_protection = CHARGE_OVERVOLTAGE 44 bms_hardware.current = charge_current 45 bms_hardware.termination_current = termination_current 46 bms_hardware.max_time = max_time 47 bms_hardware.sample_interval = sample_interval 48 bms_hardware.minimum_readings = minimum_readings 49 50 # Run the Charge cycle 51 bms_hardware.run_li_charge_cycle() 52 53 54def standard_discharge( 55 discharge_current: float = 0.800, 56 max_time: int = 7 * 24 * 3600, 57 sample_interval: int = SAMPLE_INTERVAL, 58 discharge_voltage: float = 10, 59 discharge_resistance: float = 10, 60 discharge_type: DischargeType = DischargeType.CONSTANT_CURRENT, 61): 62 """Discharge cells / pack.""" 63 bms_hardware.voltage = discharge_voltage 64 bms_hardware.uv_protection = bms_hardware.voltage - 0.100 # 100mV below voltage cutoff 65 bms_hardware.current = discharge_current 66 bms_hardware.discharge_type = discharge_type 67 bms_hardware.max_time = max_time 68 bms_hardware.sample_interval = sample_interval 69 bms_hardware.resistance = discharge_resistance 70 71 # Run the discharge cycle, returning the capacity 72 capacity = bms_hardware.run_discharge_cycle() 73 logger.write_info_to_report(f"Discharge complete, capacity was {capacity * 1000.0} mAh") 74 return capacity 75 76 77@test_step 78@pytest.mark.skipif(STEP_END < 1 or STEP_START > 1, reason=f"Starting on step {STEP_START}") 79def test_step_1_arts(): 80 """ 81 Charge 800 mA, 50400 seconds 82 Rest for 2 hours 83 """ 84 with suppress(TimeoutExceededError): 85 standard_charge( 86 sample_interval=SAMPLE_INTERVAL, 87 charge_current=0.800, 88 termination_current=-0.05, 89 charge_voltage=CHARGE_VOLTAGE, 90 max_time=16 * 3600, 91 ) 92 93 bms_hardware.max_time = 2 * 3600 # 2 hours 94 bms_hardware.sample_interval = SAMPLE_INTERVAL 95 bms_hardware.run_resting_cycle() 96 97 98@test_step 99@pytest.mark.skipif(STEP_END < 1.5 or STEP_START > 1.5, reason=f"Starting on step {STEP_START}") 100def test_step_1_5_arts(): 101 """Discharge at 17 / 20 ohms for 5 hours""" 102 with suppress(TimeoutExceededError): 103 standard_discharge( 104 sample_interval=SAMPLE_INTERVAL, 105 discharge_resistance=17 if TYPE == "17s" else 20, 106 discharge_type=DischargeType.CONSTANT_RESISTANCE, 107 discharge_voltage=-1, 108 max_time=5 * 3600, 109 ) 110 111 112@test_step 113@pytest.mark.skipif(STEP_END < 2 or STEP_START > 2, reason=f"Starting on step {STEP_START}") 114def test_step_2_arts(): 115 """ 116 Charge 800 mA, 50400 seconds 117 Rest for 2 hours 118 Discharge at 3.4 / 4 ohms for 30 minutes 119 """ 120 with suppress(TimeoutExceededError): 121 standard_charge( 122 sample_interval=SAMPLE_INTERVAL, 123 charge_current=0.800, 124 termination_current=-0.05, 125 charge_voltage=CHARGE_VOLTAGE, 126 max_time=16 * 3600, 127 ) 128 129 bms_hardware.max_time = 2 * 3600 # 2 hours 130 bms_hardware.sample_interval = SAMPLE_INTERVAL 131 bms_hardware.run_resting_cycle() 132 133 with suppress(TimeoutExceededError): 134 standard_discharge( 135 sample_interval=SAMPLE_INTERVAL, 136 discharge_resistance=3.4 if TYPE == "17s" else 4, 137 discharge_type=DischargeType.CONSTANT_RESISTANCE, 138 discharge_voltage=-1, 139 max_time=1800, 140 ) 141 142 143@test_step 144@pytest.mark.skipif(STEP_END < 3 or STEP_START > 3, reason=f"Starting on step {STEP_START}") 145def test_step_3_arts(): 146 """ 147 Charge 800 mA, 50400 seconds 148 Rest for 2 hours 149 Discharge at 2.04 / 2.4 ohms for 30 minutes 150 """ 151 with suppress(TimeoutExceededError): 152 standard_charge( 153 sample_interval=SAMPLE_INTERVAL, 154 charge_current=0.800, 155 termination_current=-0.05, 156 charge_voltage=CHARGE_VOLTAGE, 157 max_time=16 * 3600, 158 ) 159 160 bms_hardware.max_time = 2 * 3600 # 2 hours 161 bms_hardware.sample_interval = SAMPLE_INTERVAL 162 bms_hardware.run_resting_cycle() 163 164 with suppress(TimeoutExceededError): 165 standard_discharge( 166 sample_interval=SAMPLE_INTERVAL, 167 discharge_resistance=2.04 if TYPE == "17s" else 2.4, 168 discharge_type=DischargeType.CONSTANT_RESISTANCE, 169 discharge_voltage=-1, 170 max_time=1800, 171 ) 172 173 174@test_step 175@pytest.mark.skipif(STEP_END < 4 or STEP_START > 4, reason=f"Starting on step {STEP_START}") 176def test_step_4_arts(): 177 """ 178 Charge at 800 mA for 50400 seconds 179 Let pack rest for 48 hours (Measure and record pack voltage after a 48-hour rest) 180 Measure pack impedance 181 Discharge at 800 mA down to 17V / 20V 182 """ 183 # Charge 184 with suppress(TimeoutExceededError): 185 standard_charge( 186 sample_interval=SAMPLE_INTERVAL, 187 charge_current=0.800, 188 termination_current=-0.05, 189 charge_voltage=CHARGE_VOLTAGE, 190 max_time=16 * 3600, 191 ) 192 193 # Rest 194 bms_hardware.max_time = 2 * 3600 195 bms_hardware.sample_interval = SAMPLE_INTERVAL 196 bms_hardware.run_resting_cycle() 197 198 # Measure impedance 199 pulse_current = 1.0 200 raw_voltage_data = bms_hardware.csv.ocv.measure_impedance_data(pulse_current) 201 impedance = bms_hardware.csv.ocv.calculate_impedance(raw_voltage_data, pulse_current) 202 bms_hardware.csv.raw_impedance.record(impedance, raw_voltage_data) 203 logger.write_result_to_html_report(f"Impedance: {impedance} mΩ") 204 205 # Discharge 206 standard_discharge( 207 sample_interval=SAMPLE_INTERVAL, discharge_current=0.800, discharge_voltage=17 if TYPE == "17s" else 20 208 ) 209 210 211@recondition_step 212@pytest.mark.skipif(STEP_END < 1 or STEP_START > 1, reason=f"Starting on step {STEP_START}") 213def test_step_1_arts_recondition(): 214 """ 215 Discharge at 333 mA to 17V / 20V 216 """ 217 standard_discharge( 218 sample_interval=SAMPLE_INTERVAL, discharge_current=0.333, discharge_voltage=17 if TYPE == "17s" else 20 219 ) 220 221 222@recondition_step 223@pytest.mark.skipif(STEP_END < 2 or STEP_START > 2, reason=f"Starting on step {STEP_START}") 224def test_step_2_arts_recondition_step(): 225 """ 226 Charge 800 mA, 50400 seconds 227 """ 228 with suppress(TimeoutExceededError): 229 standard_charge( 230 sample_interval=SAMPLE_INTERVAL, 231 charge_current=0.800, 232 termination_current=-0.05, 233 charge_voltage=CHARGE_VOLTAGE, 234 max_time=50400, 235 ) 236 237 238@recondition_step 239@pytest.mark.skipif(STEP_END < 3 or STEP_START > 3, reason=f"Starting on step {STEP_START}") 240def test_step_3_arts_recondition(): 241 """ 242 Discharge at 333 mA to 17V / 20V 243 """ 244 standard_discharge( 245 sample_interval=SAMPLE_INTERVAL, discharge_current=0.333, discharge_voltage=17 if TYPE == "17s" else 20 246 ) 247 248 249@recondition_step 250@pytest.mark.skipif(STEP_END < 4 or STEP_START > 4, reason=f"Starting on step {STEP_START}") 251def test_step_4_arts_recondition_step(): 252 """ 253 Charge 800 mA, 50400 seconds 254 """ 255 with suppress(TimeoutExceededError): 256 standard_charge( 257 sample_interval=SAMPLE_INTERVAL, 258 charge_current=0.800, 259 termination_current=-0.05, 260 charge_voltage=CHARGE_VOLTAGE, 261 max_time=50400, 262 ) 263 264 265@recondition_step 266@pytest.mark.skipif(STEP_END < 5 or STEP_START > 5, reason=f"Starting on step {STEP_START}") 267def test_step_5_arts_recondition(): 268 """ 269 Discharge at 333 mA to 17V / 20V 270 """ 271 standard_discharge( 272 sample_interval=SAMPLE_INTERVAL, discharge_current=0.333, discharge_voltage=17 if TYPE == "17s" else 20 273 )
34def standard_charge( 35 charge_current: float = 0.800, 36 max_time: int = 16 * 3600, 37 sample_interval: int = SAMPLE_INTERVAL, 38 minimum_readings: int = 3, 39 charge_voltage: float = CHARGE_VOLTAGE, 40 termination_current: float = -0.05, # Wait for timeout instead 41): 42 """Charge cells / pack.""" 43 bms_hardware.voltage = charge_voltage 44 bms_hardware.ov_protection = CHARGE_OVERVOLTAGE 45 bms_hardware.current = charge_current 46 bms_hardware.termination_current = termination_current 47 bms_hardware.max_time = max_time 48 bms_hardware.sample_interval = sample_interval 49 bms_hardware.minimum_readings = minimum_readings 50 51 # Run the Charge cycle 52 bms_hardware.run_li_charge_cycle()
Charge cells / pack.
55def standard_discharge( 56 discharge_current: float = 0.800, 57 max_time: int = 7 * 24 * 3600, 58 sample_interval: int = SAMPLE_INTERVAL, 59 discharge_voltage: float = 10, 60 discharge_resistance: float = 10, 61 discharge_type: DischargeType = DischargeType.CONSTANT_CURRENT, 62): 63 """Discharge cells / pack.""" 64 bms_hardware.voltage = discharge_voltage 65 bms_hardware.uv_protection = bms_hardware.voltage - 0.100 # 100mV below voltage cutoff 66 bms_hardware.current = discharge_current 67 bms_hardware.discharge_type = discharge_type 68 bms_hardware.max_time = max_time 69 bms_hardware.sample_interval = sample_interval 70 bms_hardware.resistance = discharge_resistance 71 72 # Run the discharge cycle, returning the capacity 73 capacity = bms_hardware.run_discharge_cycle() 74 logger.write_info_to_report(f"Discharge complete, capacity was {capacity * 1000.0} mAh") 75 return capacity
Discharge cells / pack.
78@test_step 79@pytest.mark.skipif(STEP_END < 1 or STEP_START > 1, reason=f"Starting on step {STEP_START}") 80def test_step_1_arts(): 81 """ 82 Charge 800 mA, 50400 seconds 83 Rest for 2 hours 84 """ 85 with suppress(TimeoutExceededError): 86 standard_charge( 87 sample_interval=SAMPLE_INTERVAL, 88 charge_current=0.800, 89 termination_current=-0.05, 90 charge_voltage=CHARGE_VOLTAGE, 91 max_time=16 * 3600, 92 ) 93 94 bms_hardware.max_time = 2 * 3600 # 2 hours 95 bms_hardware.sample_interval = SAMPLE_INTERVAL 96 bms_hardware.run_resting_cycle()
Charge 800 mA, 50400 seconds Rest for 2 hours
99@test_step 100@pytest.mark.skipif(STEP_END < 1.5 or STEP_START > 1.5, reason=f"Starting on step {STEP_START}") 101def test_step_1_5_arts(): 102 """Discharge at 17 / 20 ohms for 5 hours""" 103 with suppress(TimeoutExceededError): 104 standard_discharge( 105 sample_interval=SAMPLE_INTERVAL, 106 discharge_resistance=17 if TYPE == "17s" else 20, 107 discharge_type=DischargeType.CONSTANT_RESISTANCE, 108 discharge_voltage=-1, 109 max_time=5 * 3600, 110 )
Discharge at 17 / 20 ohms for 5 hours
113@test_step 114@pytest.mark.skipif(STEP_END < 2 or STEP_START > 2, reason=f"Starting on step {STEP_START}") 115def test_step_2_arts(): 116 """ 117 Charge 800 mA, 50400 seconds 118 Rest for 2 hours 119 Discharge at 3.4 / 4 ohms for 30 minutes 120 """ 121 with suppress(TimeoutExceededError): 122 standard_charge( 123 sample_interval=SAMPLE_INTERVAL, 124 charge_current=0.800, 125 termination_current=-0.05, 126 charge_voltage=CHARGE_VOLTAGE, 127 max_time=16 * 3600, 128 ) 129 130 bms_hardware.max_time = 2 * 3600 # 2 hours 131 bms_hardware.sample_interval = SAMPLE_INTERVAL 132 bms_hardware.run_resting_cycle() 133 134 with suppress(TimeoutExceededError): 135 standard_discharge( 136 sample_interval=SAMPLE_INTERVAL, 137 discharge_resistance=3.4 if TYPE == "17s" else 4, 138 discharge_type=DischargeType.CONSTANT_RESISTANCE, 139 discharge_voltage=-1, 140 max_time=1800, 141 )
Charge 800 mA, 50400 seconds Rest for 2 hours Discharge at 3.4 / 4 ohms for 30 minutes
144@test_step 145@pytest.mark.skipif(STEP_END < 3 or STEP_START > 3, reason=f"Starting on step {STEP_START}") 146def test_step_3_arts(): 147 """ 148 Charge 800 mA, 50400 seconds 149 Rest for 2 hours 150 Discharge at 2.04 / 2.4 ohms for 30 minutes 151 """ 152 with suppress(TimeoutExceededError): 153 standard_charge( 154 sample_interval=SAMPLE_INTERVAL, 155 charge_current=0.800, 156 termination_current=-0.05, 157 charge_voltage=CHARGE_VOLTAGE, 158 max_time=16 * 3600, 159 ) 160 161 bms_hardware.max_time = 2 * 3600 # 2 hours 162 bms_hardware.sample_interval = SAMPLE_INTERVAL 163 bms_hardware.run_resting_cycle() 164 165 with suppress(TimeoutExceededError): 166 standard_discharge( 167 sample_interval=SAMPLE_INTERVAL, 168 discharge_resistance=2.04 if TYPE == "17s" else 2.4, 169 discharge_type=DischargeType.CONSTANT_RESISTANCE, 170 discharge_voltage=-1, 171 max_time=1800, 172 )
Charge 800 mA, 50400 seconds Rest for 2 hours Discharge at 2.04 / 2.4 ohms for 30 minutes
175@test_step 176@pytest.mark.skipif(STEP_END < 4 or STEP_START > 4, reason=f"Starting on step {STEP_START}") 177def test_step_4_arts(): 178 """ 179 Charge at 800 mA for 50400 seconds 180 Let pack rest for 48 hours (Measure and record pack voltage after a 48-hour rest) 181 Measure pack impedance 182 Discharge at 800 mA down to 17V / 20V 183 """ 184 # Charge 185 with suppress(TimeoutExceededError): 186 standard_charge( 187 sample_interval=SAMPLE_INTERVAL, 188 charge_current=0.800, 189 termination_current=-0.05, 190 charge_voltage=CHARGE_VOLTAGE, 191 max_time=16 * 3600, 192 ) 193 194 # Rest 195 bms_hardware.max_time = 2 * 3600 196 bms_hardware.sample_interval = SAMPLE_INTERVAL 197 bms_hardware.run_resting_cycle() 198 199 # Measure impedance 200 pulse_current = 1.0 201 raw_voltage_data = bms_hardware.csv.ocv.measure_impedance_data(pulse_current) 202 impedance = bms_hardware.csv.ocv.calculate_impedance(raw_voltage_data, pulse_current) 203 bms_hardware.csv.raw_impedance.record(impedance, raw_voltage_data) 204 logger.write_result_to_html_report(f"Impedance: {impedance} mΩ") 205 206 # Discharge 207 standard_discharge( 208 sample_interval=SAMPLE_INTERVAL, discharge_current=0.800, discharge_voltage=17 if TYPE == "17s" else 20 209 )
Charge at 800 mA for 50400 seconds Let pack rest for 48 hours (Measure and record pack voltage after a 48-hour rest) Measure pack impedance Discharge at 800 mA down to 17V / 20V
212@recondition_step 213@pytest.mark.skipif(STEP_END < 1 or STEP_START > 1, reason=f"Starting on step {STEP_START}") 214def test_step_1_arts_recondition(): 215 """ 216 Discharge at 333 mA to 17V / 20V 217 """ 218 standard_discharge( 219 sample_interval=SAMPLE_INTERVAL, discharge_current=0.333, discharge_voltage=17 if TYPE == "17s" else 20 220 )
Discharge at 333 mA to 17V / 20V
223@recondition_step 224@pytest.mark.skipif(STEP_END < 2 or STEP_START > 2, reason=f"Starting on step {STEP_START}") 225def test_step_2_arts_recondition_step(): 226 """ 227 Charge 800 mA, 50400 seconds 228 """ 229 with suppress(TimeoutExceededError): 230 standard_charge( 231 sample_interval=SAMPLE_INTERVAL, 232 charge_current=0.800, 233 termination_current=-0.05, 234 charge_voltage=CHARGE_VOLTAGE, 235 max_time=50400, 236 )
Charge 800 mA, 50400 seconds
239@recondition_step 240@pytest.mark.skipif(STEP_END < 3 or STEP_START > 3, reason=f"Starting on step {STEP_START}") 241def test_step_3_arts_recondition(): 242 """ 243 Discharge at 333 mA to 17V / 20V 244 """ 245 standard_discharge( 246 sample_interval=SAMPLE_INTERVAL, discharge_current=0.333, discharge_voltage=17 if TYPE == "17s" else 20 247 )
Discharge at 333 mA to 17V / 20V
250@recondition_step 251@pytest.mark.skipif(STEP_END < 4 or STEP_START > 4, reason=f"Starting on step {STEP_START}") 252def test_step_4_arts_recondition_step(): 253 """ 254 Charge 800 mA, 50400 seconds 255 """ 256 with suppress(TimeoutExceededError): 257 standard_charge( 258 sample_interval=SAMPLE_INTERVAL, 259 charge_current=0.800, 260 termination_current=-0.05, 261 charge_voltage=CHARGE_VOLTAGE, 262 max_time=50400, 263 )
Charge 800 mA, 50400 seconds
266@recondition_step 267@pytest.mark.skipif(STEP_END < 5 or STEP_START > 5, reason=f"Starting on step {STEP_START}") 268def test_step_5_arts_recondition(): 269 """ 270 Discharge at 333 mA to 17V / 20V 271 """ 272 standard_discharge( 273 sample_interval=SAMPLE_INTERVAL, discharge_current=0.333, discharge_voltage=17 if TYPE == "17s" else 20 274 )
Discharge at 333 mA to 17V / 20V