Skip to content

Commit

Permalink
Add support for expressions (#157)
Browse files Browse the repository at this point in the history
  • Loading branch information
fred-labs committed Aug 14, 2024
1 parent 7532420 commit 3cdb085
Show file tree
Hide file tree
Showing 12 changed files with 462 additions and 78 deletions.
2 changes: 1 addition & 1 deletion docs/openscenario2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Element Tag Support Notes
``enum`` :raw-html:`✅`
``event`` :raw-html:`✅`
``every`` :raw-html:`❌`
``expression`` :raw-html:`❌`
``expression`` :raw-html:`✅`
``extend`` :raw-html:`❌`
``external`` :raw-html:`❌`
``fall`` :raw-html:`❌`
Expand Down
4 changes: 2 additions & 2 deletions scenario_coverage/scenario_coverage/scenario_variation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import py_trees
from scenario_execution.model.osc2_parser import OpenScenario2Parser
from scenario_execution.model.model_resolver import resolve_internal_model
from scenario_execution.model.types import RelationExpression, ListExpression, FieldAccessExpression, Expression, print_tree, serialize, to_string
from scenario_execution.model.types import RelationExpression, ListExpression, FieldAccessExpression, ModelExpression, print_tree, serialize, to_string
from scenario_execution.utils.logging import Logger


Expand Down Expand Up @@ -138,7 +138,7 @@ def save_resulting_scenarios(self, models):
# create description
variation_descriptions = []
for descr, entry in model[1]:
if isinstance(entry, Expression):
if isinstance(entry, ModelExpression):
val = None
for child in entry.get_children():
if not isinstance(child, FieldAccessExpression):
Expand Down
40 changes: 32 additions & 8 deletions scenario_execution/scenario_execution/model/model_to_py_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@
import py_trees
from py_trees.common import Access, Status
from pkg_resources import iter_entry_points

import inspect

from scenario_execution.model.types import ActionDeclaration, EventReference, FunctionApplicationExpression, ModifierInvocation, ScenarioDeclaration, DoMember, WaitDirective, EmitDirective, BehaviorInvocation, EventCondition, EventDeclaration, RelationExpression, LogicalExpression, ElapsedExpression, PhysicalLiteral, ModifierDeclaration
from scenario_execution.model.types import KeepConstraintDeclaration, visit_expression, ActionDeclaration, BinaryExpression, EventReference, Expression, FunctionApplicationExpression, ModifierInvocation, ScenarioDeclaration, DoMember, WaitDirective, EmitDirective, BehaviorInvocation, EventCondition, EventDeclaration, RelationExpression, LogicalExpression, ElapsedExpression, PhysicalLiteral, ModifierDeclaration
from scenario_execution.model.model_base_visitor import ModelBaseVisitor
from scenario_execution.model.error import OSC2ParsingError
from scenario_execution.actions.base_action import BaseAction
Expand Down Expand Up @@ -103,6 +102,20 @@ def update(self):
return Status.SUCCESS


class ExpressionBehavior(py_trees.behaviour.Behaviour):

def __init__(self, name: "ExpressionBehavior", expression: Expression):
super().__init__(name)

self.expression = expression

def update(self):
if self.expression.eval():
return Status.SUCCESS
else:
return Status.RUNNING


class ModelToPyTree(object):

def __init__(self, logger):
Expand All @@ -122,6 +135,7 @@ class BehaviorInit(ModelBaseVisitor):
def __init__(self, logger, tree) -> None:
super().__init__()
self.logger = logger
self.blackboard = None
if not isinstance(tree, py_trees.composites.Sequence):
raise ValueError("ModelToPyTree requires a py-tree sequence as input")
self.tree = tree
Expand Down Expand Up @@ -348,19 +362,25 @@ def visit_event_reference(self, node: EventReference):
def visit_event_condition(self, node: EventCondition):
expression = ""
for child in node.get_children():
if isinstance(child, RelationExpression):
raise NotImplementedError()
elif isinstance(child, LogicalExpression):
raise NotImplementedError()
if isinstance(child, (RelationExpression, LogicalExpression)):
expression = ExpressionBehavior(name=node.get_ctx()[2], expression=self.visit(child))
elif isinstance(child, ElapsedExpression):
elapsed_condition = self.visit_elapsed_expression(child)
expression = py_trees.timers.Timer(
name=f"wait {elapsed_condition}s", duration=float(elapsed_condition))
expression = py_trees.timers.Timer(name=f"wait {elapsed_condition}s", duration=float(elapsed_condition))
else:
raise OSC2ParsingError(
msg=f'Invalid event condition {child}', context=node.get_ctx())
return expression

def visit_relation_expression(self, node: RelationExpression):
return visit_expression(node, self.blackboard)

def visit_logical_expression(self, node: LogicalExpression):
return visit_expression(node, self.blackboard)

def visit_binary_expression(self, node: BinaryExpression):
return visit_expression(node, self.blackboard)

def visit_elapsed_expression(self, node: ElapsedExpression):
elem = node.find_first_child_of_type(PhysicalLiteral)
if not elem:
Expand Down Expand Up @@ -389,3 +409,7 @@ def visit_modifier_invocation(self, node: ModifierInvocation):
self.create_decorator(node.modifier, resolved_values)
except ValueError as e:
raise OSC2ParsingError(msg=f'ModifierDeclaration {e}.', context=node.get_ctx()) from e

def visit_keep_constraint_declaration(self, node: KeepConstraintDeclaration):
# skip relation-expression
pass
Loading

0 comments on commit 3cdb085

Please sign in to comment.