autotest: allow message hooks to be instances of a MessageHook class
allows state to be encapsulated within the object rather than simply in the same scope / closure of the method being called. Will allow easier re-use of these blocks
This commit is contained in:
parent
0c7a527ad3
commit
eb539f2c4a
@ -8,7 +8,6 @@ from __future__ import print_function
|
|||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import time
|
|
||||||
|
|
||||||
from pymavlink import quaternion
|
from pymavlink import quaternion
|
||||||
from pymavlink import mavutil
|
from pymavlink import mavutil
|
||||||
@ -2136,42 +2135,18 @@ class AutoTestPlane(vehicle_test_suite.TestSuite):
|
|||||||
self.fly_home_land_and_disarm()
|
self.fly_home_land_and_disarm()
|
||||||
|
|
||||||
def deadreckoning_main(self, disable_airspeed_sensor=False):
|
def deadreckoning_main(self, disable_airspeed_sensor=False):
|
||||||
|
self.context_push()
|
||||||
self.set_parameter("EK3_OPTIONS", 1)
|
self.set_parameter("EK3_OPTIONS", 1)
|
||||||
self.set_parameter("AHRS_OPTIONS", 3)
|
self.set_parameter("AHRS_OPTIONS", 3)
|
||||||
self.set_parameter("LOG_REPLAY", 1)
|
self.set_parameter("LOG_REPLAY", 1)
|
||||||
self.reboot_sitl()
|
self.reboot_sitl()
|
||||||
self.wait_ready_to_arm()
|
self.wait_ready_to_arm()
|
||||||
self.gpi = None
|
|
||||||
self.simstate = None
|
|
||||||
self.last_print = 0
|
|
||||||
self.max_divergence = 0
|
|
||||||
|
|
||||||
def validate_global_position_int_against_simstate(mav, m):
|
if disable_airspeed_sensor:
|
||||||
if m.get_type() == 'GLOBAL_POSITION_INT':
|
max_allowed_divergence = 300
|
||||||
self.gpi = m
|
else:
|
||||||
elif m.get_type() == 'SIMSTATE':
|
max_allowed_divergence = 150
|
||||||
self.simstate = m
|
self.install_message_hook_context(vehicle_test_suite.TestSuite.ValidateGlobalPositionIntAgainstSimState(self, max_allowed_divergence=max_allowed_divergence)) # noqa
|
||||||
if self.gpi is None:
|
|
||||||
return
|
|
||||||
if self.simstate is None:
|
|
||||||
return
|
|
||||||
divergence = self.get_distance_int(self.gpi, self.simstate)
|
|
||||||
if disable_airspeed_sensor:
|
|
||||||
max_allowed_divergence = 300
|
|
||||||
else:
|
|
||||||
max_allowed_divergence = 150
|
|
||||||
if (time.time() - self.last_print > 1 or
|
|
||||||
divergence > self.max_divergence):
|
|
||||||
self.progress("position-estimate-divergence=%fm" % (divergence,))
|
|
||||||
self.last_print = time.time()
|
|
||||||
if divergence > self.max_divergence:
|
|
||||||
self.max_divergence = divergence
|
|
||||||
if divergence > max_allowed_divergence:
|
|
||||||
raise NotAchievedException(
|
|
||||||
"global-position-int diverged from simstate by %fm (max=%fm" %
|
|
||||||
(divergence, max_allowed_divergence,))
|
|
||||||
|
|
||||||
self.install_message_hook(validate_global_position_int_against_simstate)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# wind is from the West:
|
# wind is from the West:
|
||||||
@ -2232,9 +2207,11 @@ class AutoTestPlane(vehicle_test_suite.TestSuite):
|
|||||||
raise NotAchievedException("GPS use re-started %f sec after jamming stopped" % time_since_jamming_stopped)
|
raise NotAchievedException("GPS use re-started %f sec after jamming stopped" % time_since_jamming_stopped)
|
||||||
self.set_rc(3, 1000)
|
self.set_rc(3, 1000)
|
||||||
self.fly_home_land_and_disarm()
|
self.fly_home_land_and_disarm()
|
||||||
self.progress("max-divergence: %fm" % (self.max_divergence,))
|
|
||||||
finally:
|
finally:
|
||||||
self.remove_message_hook(validate_global_position_int_against_simstate)
|
pass
|
||||||
|
|
||||||
|
self.context_pop()
|
||||||
|
self.reboot_sitl()
|
||||||
|
|
||||||
def Deadreckoning(self):
|
def Deadreckoning(self):
|
||||||
'''Test deadreckoning support'''
|
'''Test deadreckoning support'''
|
||||||
|
@ -1832,6 +1832,7 @@ class TestSuite(ABC):
|
|||||||
self.terrain_data_messages_sent = 0 # count of messages back
|
self.terrain_data_messages_sent = 0 # count of messages back
|
||||||
self.dronecan_tests = dronecan_tests
|
self.dronecan_tests = dronecan_tests
|
||||||
self.statustext_id = 1
|
self.statustext_id = 1
|
||||||
|
self.message_hooks = [] # functions or MessageHook instances
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
if self.rc_thread is not None:
|
if self.rc_thread is not None:
|
||||||
@ -3325,6 +3326,71 @@ class TestSuite(ABC):
|
|||||||
return
|
return
|
||||||
self.drain_all_pexpects()
|
self.drain_all_pexpects()
|
||||||
|
|
||||||
|
class MessageHook():
|
||||||
|
'''base class for objects that watch the message stream and check for
|
||||||
|
validity of fields'''
|
||||||
|
def __init__(self, suite):
|
||||||
|
self.suite = suite
|
||||||
|
|
||||||
|
def process(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def progress_prefix(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def progress(self, string):
|
||||||
|
string = self.progress_prefix() + string
|
||||||
|
self.suite.progress(string)
|
||||||
|
|
||||||
|
class ValidateIntPositionAgainstSimState(MessageHook):
|
||||||
|
'''monitors a message containing a position containing lat/lng in 1e7,
|
||||||
|
makes sure it stays close to SIMSTATE'''
|
||||||
|
def __init__(self, suite, other_int_message_name, max_allowed_divergence=150):
|
||||||
|
super(TestSuite.ValidateIntPositionAgainstSimState, self).__init__(suite)
|
||||||
|
self.other_int_message_name = other_int_message_name
|
||||||
|
self.max_allowed_divergence = max_allowed_divergence
|
||||||
|
self.max_divergence = 0
|
||||||
|
self.gpi = None
|
||||||
|
self.simstate = None
|
||||||
|
self.last_print = 0
|
||||||
|
self.min_print_interval = 1 # seconds
|
||||||
|
|
||||||
|
def progress_prefix(self):
|
||||||
|
return "VIPASS: "
|
||||||
|
|
||||||
|
def process(self, mav, m):
|
||||||
|
if m.get_type() == self.other_int_message_name:
|
||||||
|
self.gpi = m
|
||||||
|
elif m.get_type() == 'SIMSTATE':
|
||||||
|
self.simstate = m
|
||||||
|
if self.gpi is None:
|
||||||
|
return
|
||||||
|
if self.simstate is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
divergence = self.suite.get_distance_int(self.gpi, self.simstate)
|
||||||
|
if (time.time() - self.last_print > self.min_print_interval or
|
||||||
|
divergence > self.max_divergence):
|
||||||
|
self.progress(f"distance(SIMSTATE,{self.other_int_message_name})={divergence:.5f}m")
|
||||||
|
self.last_print = time.time()
|
||||||
|
if divergence > self.max_divergence:
|
||||||
|
self.max_divergence = divergence
|
||||||
|
if divergence > self.max_allowed_divergence:
|
||||||
|
raise NotAchievedException(
|
||||||
|
"%s diverged from simstate by %fm (max=%fm" %
|
||||||
|
(self.other_int_message_name, divergence, self.max_allowed_divergence,))
|
||||||
|
|
||||||
|
def hook_removed(self):
|
||||||
|
self.progress(f"Maximum divergence was {self.max_divergence}m (max={self.max_allowed_divergence}m)")
|
||||||
|
|
||||||
|
class ValidateGlobalPositionIntAgainstSimState(ValidateIntPositionAgainstSimState):
|
||||||
|
def __init__(self, suite, **kwargs):
|
||||||
|
super(TestSuite.ValidateGlobalPositionIntAgainstSimState, self).__init__(suite, 'GLOBAL_POSITION_INT', **kwargs)
|
||||||
|
|
||||||
|
class ValidateAHRS3AgainstSimState(ValidateIntPositionAgainstSimState):
|
||||||
|
def __init__(self, suite, **kwargs):
|
||||||
|
super(TestSuite.ValidateAHRS3AgainstSimState, self).__init__(suite, 'AHRS3', **kwargs)
|
||||||
|
|
||||||
def message_hook(self, mav, msg):
|
def message_hook(self, mav, msg):
|
||||||
"""Called as each mavlink msg is received."""
|
"""Called as each mavlink msg is received."""
|
||||||
# print("msg: %s" % str(msg))
|
# print("msg: %s" % str(msg))
|
||||||
@ -3336,6 +3402,13 @@ class TestSuite(ABC):
|
|||||||
self.idle_hook(mav)
|
self.idle_hook(mav)
|
||||||
self.do_heartbeats()
|
self.do_heartbeats()
|
||||||
|
|
||||||
|
for h in self.message_hooks:
|
||||||
|
if isinstance(h, TestSuite.MessageHook):
|
||||||
|
h.process(mav, msg)
|
||||||
|
continue
|
||||||
|
# assume it's a function
|
||||||
|
h(mav, msg)
|
||||||
|
|
||||||
def send_message_hook(self, msg, x):
|
def send_message_hook(self, msg, x):
|
||||||
self.write_msg_to_tlog(msg)
|
self.write_msg_to_tlog(msg)
|
||||||
|
|
||||||
@ -4643,14 +4716,14 @@ class TestSuite(ABC):
|
|||||||
return wploader.count()
|
return wploader.count()
|
||||||
|
|
||||||
def install_message_hook(self, hook):
|
def install_message_hook(self, hook):
|
||||||
self.mav.message_hooks.append(hook)
|
self.message_hooks.append(hook)
|
||||||
|
|
||||||
def install_message_hook_context(self, hook):
|
def install_message_hook_context(self, hook):
|
||||||
'''installs a message hook which will be removed when the context goes
|
'''installs a message hook which will be removed when the context goes
|
||||||
away'''
|
away'''
|
||||||
if self.mav is None:
|
if self.mav is None:
|
||||||
return
|
return
|
||||||
self.mav.message_hooks.append(hook)
|
self.message_hooks.append(hook)
|
||||||
self.context_get().message_hooks.append(hook)
|
self.context_get().message_hooks.append(hook)
|
||||||
|
|
||||||
def remove_message_hook(self, hook):
|
def remove_message_hook(self, hook):
|
||||||
@ -4658,7 +4731,9 @@ class TestSuite(ABC):
|
|||||||
once'''
|
once'''
|
||||||
if self.mav is None:
|
if self.mav is None:
|
||||||
return
|
return
|
||||||
self.mav.message_hooks.remove(hook)
|
self.message_hooks.remove(hook)
|
||||||
|
if isinstance(hook, TestSuite.MessageHook):
|
||||||
|
hook.hook_removed()
|
||||||
|
|
||||||
def install_example_script_context(self, scriptname):
|
def install_example_script_context(self, scriptname):
|
||||||
'''installs an example script which will be removed when the context goes
|
'''installs an example script which will be removed when the context goes
|
||||||
@ -7480,6 +7555,23 @@ class TestSuite(ABC):
|
|||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def wait_distance_between(self, series1, series2, min_distance, max_distance, timeout=30, **kwargs):
|
||||||
|
"""Wait for distance between two position series to be between two thresholds."""
|
||||||
|
def get_distance():
|
||||||
|
self.drain_mav()
|
||||||
|
m1 = self.mav.messages[series1]
|
||||||
|
m2 = self.mav.messages[series2]
|
||||||
|
return self.get_distance_int(m1, m2)
|
||||||
|
|
||||||
|
self.wait_and_maintain_range(
|
||||||
|
value_name=f"Distance({series1}, {series2})",
|
||||||
|
minimum=min_distance,
|
||||||
|
maximum=max_distance,
|
||||||
|
current_value_getter=lambda: get_distance(),
|
||||||
|
timeout=timeout,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def wait_distance(self, distance, accuracy=2, timeout=30, **kwargs):
|
def wait_distance(self, distance, accuracy=2, timeout=30, **kwargs):
|
||||||
"""Wait for flight of a given distance."""
|
"""Wait for flight of a given distance."""
|
||||||
start = self.mav.location()
|
start = self.mav.location()
|
||||||
@ -8517,7 +8609,7 @@ Also, ignores heartbeats not from our target system'''
|
|||||||
|
|
||||||
tee = TeeBoth(test_output_filename, 'w', self.mavproxy_logfile, suppress_stdout=suppress_stdout)
|
tee = TeeBoth(test_output_filename, 'w', self.mavproxy_logfile, suppress_stdout=suppress_stdout)
|
||||||
|
|
||||||
start_message_hooks = copy.copy(self.mav.message_hooks)
|
start_message_hooks = copy.copy(self.message_hooks)
|
||||||
|
|
||||||
prettyname = "%s (%s)" % (name, desc)
|
prettyname = "%s (%s)" % (name, desc)
|
||||||
self.start_test(prettyname)
|
self.start_test(prettyname)
|
||||||
@ -8551,9 +8643,9 @@ Also, ignores heartbeats not from our target system'''
|
|||||||
ex = e
|
ex = e
|
||||||
# reset the message hooks; we've failed-via-exception and
|
# reset the message hooks; we've failed-via-exception and
|
||||||
# can't expect the hooks to have been cleaned up
|
# can't expect the hooks to have been cleaned up
|
||||||
for h in copy.copy(self.mav.message_hooks):
|
for h in copy.copy(self.message_hooks):
|
||||||
if h not in start_message_hooks:
|
if h not in start_message_hooks:
|
||||||
self.mav.message_hooks.remove(h)
|
self.message_hooks.remove(h)
|
||||||
hooks_removed = True
|
hooks_removed = True
|
||||||
self.test_timings[desc] = time.time() - start_time
|
self.test_timings[desc] = time.time() - start_time
|
||||||
reset_needed = self.contexts[-1].sitl_commandline_customised
|
reset_needed = self.contexts[-1].sitl_commandline_customised
|
||||||
@ -8640,9 +8732,9 @@ Also, ignores heartbeats not from our target system'''
|
|||||||
self.progress("Done popping extra contexts")
|
self.progress("Done popping extra contexts")
|
||||||
|
|
||||||
# make sure we don't leave around stray listeners:
|
# make sure we don't leave around stray listeners:
|
||||||
if len(self.mav.message_hooks) != len(start_message_hooks):
|
if len(self.message_hooks) != len(start_message_hooks):
|
||||||
self.progress("Stray message listeners: %s vs start %s" %
|
self.progress("Stray message listeners: %s vs start %s" %
|
||||||
(str(self.mav.message_hooks), str(start_message_hooks)))
|
(str(self.message_hooks), str(start_message_hooks)))
|
||||||
passed = False
|
passed = False
|
||||||
|
|
||||||
if passed:
|
if passed:
|
||||||
|
Loading…
Reference in New Issue
Block a user