Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions aircraft/aircraft_ws/src/mission/mission/mission_actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Mission leaf action name allowlist (import-safe offline)."""

KNOWN_ACTIONS = frozenset({
"takeoff",
"land",
"orbit",
"wait",
"offboard",
"reposition",
"speed",
"check_blackboard",
})


def is_known_mission_action(action) -> bool:
"""Return True if action is a supported mission tree leaf name."""
return isinstance(action, str) and action in KNOWN_ACTIONS
26 changes: 26 additions & 0 deletions aircraft/aircraft_ws/src/mission/mission/test_mission_actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
import unittest

try:
from mission.mission_actions import KNOWN_ACTIONS, is_known_mission_action
except ImportError: # plain unittest from this directory
from mission_actions import KNOWN_ACTIONS, is_known_mission_action


class TestMissionActions(unittest.TestCase):
def test_known(self):
for name in ("takeoff", "land", "wait", "offboard"):
self.assertTrue(is_known_mission_action(name))

def test_unknown(self):
self.assertFalse(is_known_mission_action("fly_to_moon"))
self.assertFalse(is_known_mission_action(""))
self.assertFalse(is_known_mission_action(None))
self.assertFalse(is_known_mission_action(1))

def test_frozen_set_size(self):
self.assertEqual(len(KNOWN_ACTIONS), 8)


if __name__ == "__main__":
unittest.main()
7 changes: 6 additions & 1 deletion aircraft/aircraft_ws/src/mission/mission/tree_builder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import uuid
import py_trees
from mission import behaviors
from mission.mission_actions import is_known_mission_action

def create_mission_tree(node_cfg, ros_node):
# Recursively parse a YAML dictionary node into a py_trees object
Expand All @@ -27,7 +28,11 @@ def create_mission_tree(node_cfg, ros_node):
elif action == 'check_blackboard':
return behaviors.CheckBlackboardBehavior(name, ros_node, params)
else:
ros_node.get_logger().error(f"Unknown action: {action}")
# Keep KNOWN_ACTIONS in mission_actions.py in sync with branches above
if not is_known_mission_action(action):
ros_node.get_logger().error(f"Unknown action: {action}")
else:
ros_node.get_logger().error(f"Unhandled known action branching: {action}")
return py_trees.behaviours.Failure(name=f"Unknown_{action}")

# Is it a composite node (a sequence or fallback/selector branch)?
Expand Down
Loading