autotest: add test for the CircuitStatus lua driver
pre-commit / ci (push) Canceled after 0s
test scripting / test-scripting (push) Canceled after 0s
test scripts / build (astyle-cleanliness) (push) Canceled after 0s
test scripts / build (check_autotest_options) (push) Canceled after 0s
test scripts / build (logger_metadata) (push) Canceled after 0s
test scripts / build (param-file-validation) (push) Canceled after 0s
test scripts / build (param_parse) (push) Canceled after 0s
test scripts / build (python-cleanliness) (push) Canceled after 0s
test scripts / build (shellcheck) (push) Canceled after 0s
test scripts / build (validate_board_list) (push) Canceled after 0s

adds periph_board to the quadplane-can frame so restart_SITL_frame
spawns the AP_Periph companion, driver script install helpers and a
plane test checking that scripting monitors fed from CircuitStatus
match a DroneCAN reference monitor on the same periph battery
This commit is contained in:
Andrew Tridgell
2026-08-20 10:35:46 +10:00
committed by Peter Barker
parent 1a96559e00
commit 2cd33afd17
3 changed files with 70 additions and 1 deletions
+2 -1
View File
@@ -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",
+54
View File
@@ -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
+14
View File
@@ -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: