diff --git a/Tools/autotest/pysim/vehicleinfo.json b/Tools/autotest/pysim/vehicleinfo.json index 15a0b602ca1..4b20cf33194 100644 --- a/Tools/autotest/pysim/vehicleinfo.json +++ b/Tools/autotest/pysim/vehicleinfo.json @@ -439,7 +439,8 @@ "periph_params_filename": [ "default_params/periph.parm", "default_params/quadplane-periph.parm" - ] + ], + "periph_board": "sitl_periph_universal" }, "quadplane-PPP": { "waf_target": "bin/arduplane", diff --git a/Tools/autotest/quadplane.py b/Tools/autotest/quadplane.py index 5935f0deffb..6046fc419dd 100644 --- a/Tools/autotest/quadplane.py +++ b/Tools/autotest/quadplane.py @@ -3522,6 +3522,59 @@ class AutoTestQuadPlane(vehicle_test_suite.TestSuite): self.do_RTL() + def CircuitStatusScript(self): + '''test CircuitStatus lua driver against a CAN periph''' + self.context_collect('STATUSTEXT') + + self.install_driver_script_context("CircuitStatus.lua") + + # CAN_P1_DRIVER=1 is needed so plane publishes the SITL multicast + # sim state the periph blocks on at boot. BATT_MONITOR=8 + # (DroneCAN) gives a reference monitor fed by BatteryInfo from + # the same periph battery the CircuitStatus messages come from. + self.set_parameters({ + "SCR_ENABLE": 1, + "CAN_P1_DRIVER": 1, + "BATT_MONITOR": 8, # DroneCAN + "BATT2_MONITOR": 29, # scripting + "BATT3_MONITOR": 29, # scripting + }) + self.restart_SITL_frame('quadplane-can', customisations=[]) + + # first boot of the script creates DCS_NUM_CIRCUITS; setting it + # and rebooting creates the per-circuit parameters + self.set_parameters({ + "DCS_NUM_CIRCUITS": 2, + }) + self.reboot_sitl() + self.wait_statustext("CircuitStatus: loaded 2 circuits", check_context=True, timeout=60) + + # per-circuit parameters are polled at runtime, so no further + # reboot is needed. The SITL periph sends a circuit per battery + # backend with circuit_id of instance+1; map its first battery + # to both scripting monitors + self.set_parameters({ + "DCS1_CIRCUIT_ID": 1, + "DCS1_BATT_IDX": 2, + "DCS2_CIRCUIT_ID": 1, + "DCS2_BATT_IDX": 3, + }) + + # the scripting monitors should match the DroneCAN reference + # monitor, with tolerance for float16 quantisation and sampling + # time differences. SYS_STATUS gives the pack voltage of the + # reference monitor; BATTERY_STATUS voltages[] of the DroneCAN + # monitor holds per-cell voltages so is not comparable + self.set_message_rate_hz('BATTERY_STATUS', 10) + ref = self.assert_receive_message('SYS_STATUS', timeout=10) + for instance in 1, 2: + self.wait_message_field_values('BATTERY_STATUS', { + "voltages[0]": ref.voltage_battery, + }, instance=instance, epsilon=100, timeout=60) + self.wait_message_field_values('BATTERY_STATUS', { + "current_battery": ref.current_battery, + }, instance=instance, epsilon=25, timeout=60) + def tests(self): '''return list of all tests''' @@ -3608,5 +3661,6 @@ class AutoTestQuadPlane(vehicle_test_suite.TestSuite): self.HighServoFunctionDefault, self.WPSpdChange, self.TECSThrSpikeOnModeChange, + self.CircuitStatusScript, ]) return ret diff --git a/Tools/autotest/vehicle_test_suite.py b/Tools/autotest/vehicle_test_suite.py index aaed1c56c49..06145db4f1b 100644 --- a/Tools/autotest/vehicle_test_suite.py +++ b/Tools/autotest/vehicle_test_suite.py @@ -5392,6 +5392,13 @@ class TestSuite(abc.ABC): self.install_applet_script(scriptname, **kwargs) self.context_get().installed_scripts.append(scriptname) + def install_driver_script_context(self, scriptname, install_name=None): + '''installs a driver script which will be removed when the context goes + away''' + self.install_driver_script(scriptname, install_name=install_name) + installed_name = install_name if install_name is not None else scriptname + self.context_get().installed_scripts.append(installed_name) + def rootdir(self): this_dir = os.path.dirname(__file__) return os.path.realpath(os.path.join(this_dir, "../..")) @@ -9678,6 +9685,9 @@ Also, ignores heartbeats not from our target system''' def script_applet_source_path(self, scriptname): return os.path.join(self.rootdir(), "libraries", "AP_Scripting", "applets", scriptname) + def script_driver_source_path(self, scriptname): + return os.path.join(self.rootdir(), "libraries", "AP_Scripting", "drivers", scriptname) + def script_modules_source_path(self, scriptname): return os.path.join(self.rootdir(), "libraries", "AP_Scripting", "modules", scriptname) @@ -9743,6 +9753,10 @@ Also, ignores heartbeats not from our target system''' source = self.script_applet_source_path(scriptname) self.install_script(source, scriptname, install_name=install_name) + def install_driver_script(self, scriptname, install_name=None): + source = self.script_driver_source_path(scriptname) + self.install_script(source, scriptname, install_name=install_name) + def remove_installed_script(self, scriptname): dest = self.installed_script_path(os.path.basename(scriptname)) try: