diff --git a/.github/bandit.yaml b/.github/bandit.yaml new file mode 100644 index 00000000..c91b938b --- /dev/null +++ b/.github/bandit.yaml @@ -0,0 +1,321 @@ +exclude_dirs: ['install', 'build', 'dependencies'] + +tests: + [ 'B301', 'B302', 'B303', 'B304', 'B305', 'B306', 'B308', 'B310', 'B311', 'B312', 'B313', 'B314', 'B315', 'B316', 'B317', 'B318', 'B319', 'B320', 'B321', 'B323', 'B324', 'B401', 'B402', 'B403', 'B404', 'B405', 'B406', 'B407', 'B408', 'B409', 'B410', 'B411', 'B412', 'B413'] + +skips: + [ 'B101', 'B102', 'B103', 'B104', 'B105', 'B106', 'B107', 'B108', 'B110', 'B112', 'B201', 'B501', 'B502', 'B503', 'B504', 'B505', 'B506', 'B507', 'B601', 'B602', 'B603', 'B604', 'B605', 'B606', 'B607', 'B608', 'B609', 'B610', 'B611', 'B701', 'B702', 'B703'] + +### (optional) plugin settings - some test plugins require configuration data +### that may be given here, per-plugin. All bandit test plugins have a built in +### set of sensible defaults and these will be used if no configuration is +### provided. It is not necessary to provide settings for every (or any) plugin +### if the defaults are acceptable. + +any_other_function_with_shell_equals_true: + no_shell: + - os.execl + - os.execle + - os.execlp + - os.execlpe + - os.execv + - os.execve + - os.execvp + - os.execvpe + - os.spawnl + - os.spawnle + - os.spawnlp + - os.spawnlpe + - os.spawnv + - os.spawnve + - os.spawnvp + - os.spawnvpe + - os.startfile + shell: + - os.system + - os.popen + - os.popen2 + - os.popen3 + - os.popen4 + - popen2.popen2 + - popen2.popen3 + - popen2.popen4 + - popen2.Popen3 + - popen2.Popen4 + - commands.getoutput + - commands.getstatusoutput + subprocess: + - subprocess.Popen + - subprocess.call + - subprocess.check_call + - subprocess.check_output + - subprocess.run +assert_used: + skips: [] +hardcoded_tmp_directory: + tmp_dirs: + - /tmp + - /var/tmp + - /dev/shm +linux_commands_wildcard_injection: + no_shell: + - os.execl + - os.execle + - os.execlp + - os.execlpe + - os.execv + - os.execve + - os.execvp + - os.execvpe + - os.spawnl + - os.spawnle + - os.spawnlp + - os.spawnlpe + - os.spawnv + - os.spawnve + - os.spawnvp + - os.spawnvpe + - os.startfile + shell: + - os.system + - os.popen + - os.popen2 + - os.popen3 + - os.popen4 + - popen2.popen2 + - popen2.popen3 + - popen2.popen4 + - popen2.Popen3 + - popen2.Popen4 + - commands.getoutput + - commands.getstatusoutput + subprocess: + - subprocess.Popen + - subprocess.call + - subprocess.check_call + - subprocess.check_output + - subprocess.run +ssl_with_bad_defaults: + bad_protocol_versions: + - PROTOCOL_SSLv2 + - SSLv2_METHOD + - SSLv23_METHOD + - PROTOCOL_SSLv3 + - PROTOCOL_TLSv1 + - SSLv3_METHOD + - TLSv1_METHOD + - PROTOCOL_TLSv1_1 + - TLSv1_1_METHOD +ssl_with_bad_version: + bad_protocol_versions: + - PROTOCOL_SSLv2 + - SSLv2_METHOD + - SSLv23_METHOD + - PROTOCOL_SSLv3 + - PROTOCOL_TLSv1 + - SSLv3_METHOD + - TLSv1_METHOD + - PROTOCOL_TLSv1_1 + - TLSv1_1_METHOD +start_process_with_a_shell: + no_shell: + - os.execl + - os.execle + - os.execlp + - os.execlpe + - os.execv + - os.execve + - os.execvp + - os.execvpe + - os.spawnl + - os.spawnle + - os.spawnlp + - os.spawnlpe + - os.spawnv + - os.spawnve + - os.spawnvp + - os.spawnvpe + - os.startfile + shell: + - os.system + - os.popen + - os.popen2 + - os.popen3 + - os.popen4 + - popen2.popen2 + - popen2.popen3 + - popen2.popen4 + - popen2.Popen3 + - popen2.Popen4 + - commands.getoutput + - commands.getstatusoutput + subprocess: + - subprocess.Popen + - subprocess.call + - subprocess.check_call + - subprocess.check_output + - subprocess.run +start_process_with_no_shell: + no_shell: + - os.execl + - os.execle + - os.execlp + - os.execlpe + - os.execv + - os.execve + - os.execvp + - os.execvpe + - os.spawnl + - os.spawnle + - os.spawnlp + - os.spawnlpe + - os.spawnv + - os.spawnve + - os.spawnvp + - os.spawnvpe + - os.startfile + shell: + - os.system + - os.popen + - os.popen2 + - os.popen3 + - os.popen4 + - popen2.popen2 + - popen2.popen3 + - popen2.popen4 + - popen2.Popen3 + - popen2.Popen4 + - commands.getoutput + - commands.getstatusoutput + subprocess: + - subprocess.Popen + - subprocess.call + - subprocess.check_call + - subprocess.check_output + - subprocess.run +start_process_with_partial_path: + no_shell: + - os.execl + - os.execle + - os.execlp + - os.execlpe + - os.execv + - os.execve + - os.execvp + - os.execvpe + - os.spawnl + - os.spawnle + - os.spawnlp + - os.spawnlpe + - os.spawnv + - os.spawnve + - os.spawnvp + - os.spawnvpe + - os.startfile + shell: + - os.system + - os.popen + - os.popen2 + - os.popen3 + - os.popen4 + - popen2.popen2 + - popen2.popen3 + - popen2.popen4 + - popen2.Popen3 + - popen2.Popen4 + - commands.getoutput + - commands.getstatusoutput + subprocess: + - subprocess.Popen + - subprocess.call + - subprocess.check_call + - subprocess.check_output + - subprocess.run +subprocess_popen_with_shell_equals_true: + no_shell: + - os.execl + - os.execle + - os.execlp + - os.execlpe + - os.execv + - os.execve + - os.execvp + - os.execvpe + - os.spawnl + - os.spawnle + - os.spawnlp + - os.spawnlpe + - os.spawnv + - os.spawnve + - os.spawnvp + - os.spawnvpe + - os.startfile + shell: + - os.system + - os.popen + - os.popen2 + - os.popen3 + - os.popen4 + - popen2.popen2 + - popen2.popen3 + - popen2.popen4 + - popen2.Popen3 + - popen2.Popen4 + - commands.getoutput + - commands.getstatusoutput + subprocess: + - subprocess.Popen + - subprocess.call + - subprocess.check_call + - subprocess.check_output + - subprocess.run +subprocess_without_shell_equals_true: + no_shell: + - os.execl + - os.execle + - os.execlp + - os.execlpe + - os.execv + - os.execve + - os.execvp + - os.execvpe + - os.spawnl + - os.spawnle + - os.spawnlp + - os.spawnlpe + - os.spawnv + - os.spawnve + - os.spawnvp + - os.spawnvpe + - os.startfile + shell: + - os.system + - os.popen + - os.popen2 + - os.popen3 + - os.popen4 + - popen2.popen2 + - popen2.popen3 + - popen2.popen4 + - popen2.Popen3 + - popen2.Popen4 + - commands.getoutput + - commands.getstatusoutput + subprocess: + - subprocess.Popen + - subprocess.call + - subprocess.check_call + - subprocess.check_output + - subprocess.run +try_except_continue: + check_typed_exception: false +try_except_pass: + check_typed_exception: false +weak_cryptographic_key: + weak_key_size_dsa_high: 1024 + weak_key_size_dsa_medium: 2048 + weak_key_size_ec_high: 160 + weak_key_size_ec_medium: 224 + weak_key_size_rsa_high: 1024 + weak_key_size_rsa_medium: 2048 + diff --git a/.github/linters/.pylintrc b/.github/linters/.pylintrc index 70e1beca..21e84adf 100644 --- a/.github/linters/.pylintrc +++ b/.github/linters/.pylintrc @@ -4,6 +4,6 @@ ignore-paths=.*/osc2_parsing/.* [MESSAGES CONTROL] max-line-length=140 disable=no-self-use,anomalous-backslash-in-string,too-many-arguments,too-few-public-methods,too-many-instance-attributes,redefined-variable-type,unused-argument,bad-continuation,too-many-lines,too-many-branches,locally-disabled,too-many-locals,too-many-statements,duplicate-code,too-many-nested-blocks,fixme,useless-object-inheritance,no-else-raise,no-else-break,unnecessary-pass,no-else-return,super-with-arguments,no-else-continue,bad-option-value,consider-using-dict-items,consider-using-f-string,line-too-long,wrong-import-order,missing-function-docstring,missing-class-docstring,f-string-without-interpolation,import-error,missing-module-docstring,consider-using-with,unspecified-encoding -ignored-modules=geometry_msgs,py_trees +ignored-modules=geometry_msgs,py_trees,launch variable-rgx=[a-z0-9_]{1,40}$ function-rgx=[a-z0-9_]{1,40}$ diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index d31785ec..d90d30ba 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -50,3 +50,50 @@ jobs: format: 'table' exit-code: '1' vuln-type: 'os,library' + bandit: + name: Bandit + runs-on: intellabs-01 + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Test + shell: bash + run: | + pip3 install bandit + bandit -c .github/bandit.yaml -r . + license: + name: License check + runs-on: intellabs-01 + container: + image: osrf/ros:humble-desktop + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Prepare System + shell: bash + run: | + apt update + apt install -y python3-pip + pip3 install ros-license-toolkit + apt install -y golang-go + go version + go install github.com/google/addlicense@latest + - name: Check for license tags + shell: bash + run: | + find . -type f \( -name "*.py" -o -name "*.cpp" -o -name "*.h" \) -exec "$HOME"/go/bin/addlicense -check {} + + - name: Run ros_license_toolkit for each Package + shell: bash + run: | + git config --global --add safe.directory /__w/scenario_execution/scenario_execution + find . -name "package.xml" | while IFS= read -r pkg_file; do + pkg_dir=$(dirname "$pkg_file") + pkg_name=$(basename "$pkg_dir") + if [ "$pkg_name" = "scenario_execution_rviz" ]; then + echo "Skipping package $pkg_name" + continue + fi + echo "Processing package at $pkg_dir" + ros_license_toolkit "$pkg_dir" + done + diff --git a/.github/workflows/test_build.yml b/.github/workflows/test_build.yml new file mode 100644 index 00000000..1e1ed6d2 --- /dev/null +++ b/.github/workflows/test_build.yml @@ -0,0 +1,60 @@ +--- +name: test-build +on: + # Triggers the workflow on push or pull request events but + # only for the main branch + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: +jobs: + vanilla-build: + runs-on: intellabs-01 + container: + image: osrf/ros:humble-desktop + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Setup Dependencies + run: | + apt update + apt install -y python3-pip + xargs -a deb_requirements.txt apt install -y --no-install-recommends + rosdep update --rosdistro=humble + rosdep install --rosdistro=humble --from-paths . --ignore-src -r -y + pip3 install -r requirements.txt + pip3 install pytest-cov + - name: Build + shell: bash + run: | + source /opt/ros/humble/setup.bash + colcon build --continue-on-error + source install/setup.bash + - name: Test + shell: bash + run: | + source /opt/ros/humble/setup.bash + source install/setup.bash + export -n CYCLONEDDS_URI + export ROS_DOMAIN_ID=2 + colcon test --packages-select \ + scenario_execution_base \ + scenario_execution \ + scenario_execution_gazebo \ + scenario_coverage \ + --event-handlers console_direct+ \ + --return-code-on-test-failure \ + --pytest-with-coverage \ + --pytest-args \ + --junit-xml=TEST.xml + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@283dea176069279a9076e77b548668a8e4f0c31b + if: always() + with: + files: | + scenario_execution_base//TEST.xml + scenario_execution//TEST.xml + scenario_execution_gazebo//TEST.xml + scenario_coverage//TEST.xml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..52691286 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "dependencies/py_trees_ros"] + path = dependencies/py_trees_ros + url = https://github.com/splintered-reality/py_trees_ros.git +[submodule "dependencies/py_trees"] + path = dependencies/py_trees + url = https://github.com/splintered-reality/py_trees.git diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..807128ad --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +file_finder = find . -type f $(1) -not \( -path './venv/*' -o -path './build/*' -o -path './log/*' -o -path './install/*' -o -path './dependencies/*' \) + +PY_FILES = $(call file_finder,-name "*.py") +CPP_FILES = $(call file_finder,-name "*.cpp") +H_FILES = $(call file_finder,-name "*.cpp") +C_FILES = $(call file_finder,-name "*.cpp") + +LINKCHECKDIR = build/linkcheck + +check: check_format pylint + +format: + $(PY_FILES) | xargs autopep8 --in-place --max-line-length=140 + $(CPP_FILES) | xargs clang-format -i + $(H_FILES) | xargs clang-format -i + $(C_FILES) | xargs clang-format -i + +check_format: + $(PY_FILES) | xargs autopep8 --diff --max-line-length=140 --exit-code + +pylint: + $(PY_FILES) | xargs pylint --rcfile=.github/linters/.pylintrc + +sphinx_setup: + if [ ! -d "venv" ]; then \ + python -m venv venv/; \ + . venv/bin/activate; \ + pip install -r docs/requirements.txt; \ + deactivate; \ + fi + +doc: sphinx_setup checklinks checkspelling + . venv/bin/activate && GITHUB_REF_NAME=local GITHUB_REPOSITORY=intellabs/scenario_execution python -m sphinx -b html -W docs build/html + +view_doc: doc + firefox build/html/index.html & + +checklinks: sphinx_setup + . venv/bin/activate && GITHUB_REF_NAME=local GITHUB_REPOSITORY=intellabs/scenario_execution python -m sphinx -b html -b linkcheck -W docs $(ALLSPHINXOPTS) $(LINKCHECKDIR) + @echo + @echo "Check finished. Report is in $(LINKCHECKDIR)." + +checkspelling: sphinx_setup + . venv/bin/activate && GITHUB_REF_NAME=local GITHUB_REPOSITORY=intellabs/scenario_execution python -m sphinx -b html -b spelling -W docs $(ALLSPHINXOPTS) $(LINKCHECKDIR) + @echo + @echo "Check finished. Report is in $(LINKCHECKDIR)." diff --git a/README.md b/README.md index 3c052a83..8c735dc5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,55 @@ -# Scenario_Execution +# Scenario Execution [![Super-Linter](https://github.com/IntelLabs/Scenario_Execution/actions/workflows/scan.yml/badge.svg)](https://github.com/marketplace/actions/super-linter) + +Scenario execution is a backend- and middleware-agnostic library written in Python based on the generic scenario description language [OpenSCENARIO 2](https://www.asam.net/static_downloads/public/asam-openscenario/2.0.0/welcome.html) and [pytrees](https://py-trees.readthedocs.io/en/devel/). +It reads a scenario definition from a file and then executes it, reusing available checks and actions. It is easily extendable through a library mechanism. +This separation of the scenario definition from implementation massively reduces the manual efforts of scenario creation. + +To give an impression of the functionality of scenario execution, the following animation shows an example scenario with a turtlebot-like robot in simulation using Nav2 to navigate towards a specified navigation goal in a simulated warehouse environment. +Once the robot reaches a reference position a box is spawned in front of the robot as an unmapped static obstacle that needs to be avoided. +Upon arrival of the goal position, the scenario ends and the simulation gets cleaned up. + +![scenario execution in action](docs/images/scenario.gif "in action") + +## Documentation + +Please find the documentation [here](https://intellabs.github.io/scenario_execution). + +## Setup + +### Installation from source as ROS 2 workspace + +Clone this repository, update its submodules by running: + +```bash +git submodule update --init +``` + +install the necessary dependencies: + +```bash +rosdep install --from-paths . --ignore-src +pip3 install -r requirements.txt +``` + +and build it + +```bash +colcon build +``` + +## How to run + +First, build the packages: + +```bash +colcon build +source install/setup.bash +``` + +To launch a scenario with ROS2: + +```bash +ros2 launch scenario_execution scenario_launch.py scenario:=examples/example_scenario/hello_world.osc live_tree:=True +``` diff --git a/deb_requirements.txt b/deb_requirements.txt new file mode 100644 index 00000000..f07fc8c3 --- /dev/null +++ b/deb_requirements.txt @@ -0,0 +1,5 @@ +ros-humble-turtlebot4-simulator +ros-humble-py-trees-ros-interfaces +python3-autopep8 +clang-format +pylint \ No newline at end of file diff --git a/dependencies/py_trees b/dependencies/py_trees new file mode 160000 index 00000000..8d8fc19d --- /dev/null +++ b/dependencies/py_trees @@ -0,0 +1 @@ +Subproject commit 8d8fc19d5f6c81bc2107039a7f0baa763a01bb79 diff --git a/dependencies/py_trees_ros b/dependencies/py_trees_ros new file mode 160000 index 00000000..247cf47d --- /dev/null +++ b/dependencies/py_trees_ros @@ -0,0 +1 @@ +Subproject commit 247cf47d509f827bb24464be376e8ed2ddefc0fb diff --git a/docs/actions.rst b/docs/actions.rst deleted file mode 100644 index 4fc809fd..00000000 --- a/docs/actions.rst +++ /dev/null @@ -1,3 +0,0 @@ - -Actions -======= \ No newline at end of file diff --git a/docs/architecture.rst b/docs/architecture.rst index e1ee3a10..b2abfd71 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -1,7 +1,8 @@ Architecture ============ -.. figure:: images/graphs/scenario_execution_structure.png + +.. figure:: images/scenario_execution_structure.png :alt: Architecture of Scenario Execution Architecture of Scenario Execution @@ -19,67 +20,46 @@ The scenario execution contains several sub-packages, namely The architecture aims at modularity with each package implementing a specific functionality. -Parsing -~~~~~~~ - -.. figure:: images/parsing.png - :alt: Architecture of Scenario Parsing - - Architecture of Scenario Parsing - -Modules -======= - -Scenario Execution Base Package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The “scenario_execution_base” package is the base package for scenario -execution. It provides functionalities like parsing and base classes. -For more documentation of this package, please refer to :repo_link:`scenario_execution_base/README.md`. - -Scenario Execution Package -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The “scenario_execution” package uses ROS2 as middleware and contains -ROS2-specific modules. For more documentation of this package, please -refer to :repo_link:`scenario_execution/README.md`. +Design for Modularity +--------------------- -Scenario Execution Gazebo Package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Scenario execution is designed to be easily extensible through libraries. +An example is available here: :ref:`scenario_library`. -The “scenario_execution_gazebo” package is an extension of the -“scenario_execution” package with Gazebo/AMR dependencies. For more -documentation of this package, please refer to :repo_link:`scenario_execution_gazebo/README.md`. +The entry points are defined like this: -Scenario Execution Control Package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. code-block:: -The “scenario_execution_control” package provides code to control -scenarios (in ROS2) from another application such as Rviz. For more -documentation of this package, please refer to :repo_link:`scenario_execution_control/README.md`. + entry_points={ + 'scenario_execution.actions': [ + 'custom_action = example_library.custom_action:CustomAction', + ], + 'scenario_execution.osc_libraries': [ + 'example = example_library.get_osc_library:get_example_library', + ] + } -Scenario Execution Interfaces Package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Scenario Parsing +---------------- -The “scenario_execution_interfaces” package provides ROS2 -`interfaces `__, -more specifically, messages and services, which are used to interface -ROS2 with the scenario execution control package. For more -documentation of this package, please refer to :repo_link:`scenario_execution_interfaces/README.md`. +.. figure:: images/parsing.png + :alt: Architecture of Scenario Parsing -Scenario Execution Rviz Package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Architecture of Scenario Parsing -The “scenario execution rviz” package contains code for several -`rviz `__ plugins for visualizing and -controlling scenarios when working with `ROS -2 `__. For more -documentation of this package, please refer to :repo_link:`scenario_execution_rviz/README.md`. +The Internal Model Builder, implemented as a Model Listener does an initial check of the model by checking for supported language features. The Internal Model Resolver, implemented as a Model Visitor is used for type/variable resolving and does an in depth consistency check of the model. -Scenario Execution Kubernetes Package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The “scenario_execution_kubernetes” package contains custom conditions -and actions when running scenarios inside a -`Kubernetes `__ cluster. For more documentation -of this package, please refer to :repo_link:`scenario_execution_kubernetes/README.md`. +Modules +------- + +- ``scenario_execution_base``: The base package for scenario execution. It provides the parsing of OpenSCENARIO 2 files and the conversion to py-trees. It's middleware agnostic and can therefore be used as a basis for more specific implementations (e.g. ROS). It also provides basic OpenSCENARIO 2 libraries and actions. +- ``scenario_execution``: This package uses ``scenario_execution_base`` as a basis and implements a ROS2 version of scenario execution. It provides a OpenSCENARIO 2 library with basic ROS2-related actions like publishing on a topic or calling a service. +- ``scenario_execution_control``: Provides code to control scenario execution (in ROS2) from another application such as RViz. +- ``scenario_coverage``: Provides tools to generate concrete scenarios from abstract OpenSCENARIO 2 scenario definition and execute them. +- ``scenario_execution_gazebo``: Provides a `Gazebo `_-specific OpenSCENARIO 2 library with actions. +- ``scenario_execution_interfaces``: Provides ROS2 `interfaces `__, more specifically, messages and services, which are used to interface ROS2 with the ``scenario_execution_control`` package. +- ``scenario_execution_rviz``: Contains several `rviz `__ plugins for visualizing and controlling scenarios when working with ROS2. +- ``simulation/tb4_sim_scenario``: Run `Turtlebot4 `_ within simulation, controlled by scenario execution. +- ``tools/message_modification``: ROS2 nodes to modify messages. +- ``tools/scenario_status``: Publish the current scenario status on a topic (e.g. to be capture within a ROS bag). \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 9085b258..162feabe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,8 +14,8 @@ copyright = f"{datetime.datetime.now()}, Intel" author = "Intel" -version = '0.0.0' -release = '0.0.0' +version = '1.0.0' +release = '1.0.0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration @@ -23,7 +23,7 @@ extensions = ['sphinx.ext.extlinks', 'sphinxcontrib.spelling'] -extlinks = {'repo_link': ('https://github.com/IntelLabs/scenario_execution/blob/main/%s', '%s')} +extlinks = {'repo_link': ('https://github.com/intellabs/scenario_execution/blob/main/%s', '%s')} templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] @@ -31,7 +31,7 @@ language = 'english' linkcheck_ignore = [ - r'https://github.com/IntelLabs/scenario_execution/.*', + r'https://github.com/intellabs/scenario_execution/.*', ] spelling_word_list_filename = 'dictionary.txt' diff --git a/docs/development.rst b/docs/development.rst index f93a96d1..e300616d 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -8,13 +8,13 @@ Contribute Before pushing your code, please ensure that the code formatting is correct by running: -:: +.. code-block:: bash make In case of errors you can run the autoformatter by executing: -:: +.. code-block:: bash make format @@ -23,7 +23,7 @@ Testing To run only specific tests: -:: +.. code-block:: bash #using py-test colcon build --packages-up-to scenario_execution && reset && pytest-3 -s scenario_execution/test/.py @@ -35,20 +35,22 @@ To run only specific tests: Developing and Debugging with Visual Studio Code ------------------------------------------------ -To prevent certain issues, please use the following command for building (remove /build and /install if another command was used before). +To prevent certain issues, please use the following command for building (remove `/build` and `/install`` if another command was used before). + +.. code-block:: bash + + colcon build --symlink-install -``` -colcon build --symlink-install -``` In VSCode create new debugging configuration file: Run -> "Add Configuration..." Add the following entry to the "configurations" element within the previously created `launch.json` file (replace the arguments as required): -:: + +.. code-block:: json { - "name": "scneario_execution", + "name": "scenario_execution", "type": "python", "request": "launch", "program": "./install/scenario_execution/lib/scenario_execution/scenario_execution", @@ -57,23 +59,38 @@ Add the following entry to the "configurations" element within the previously cr "args": ["-o", "TEST_SCENARIO.osc"], } +Create an `.env` file by executing: -To execute the debug configuration either switch to debug view (on the left) and click on "play" or press F5. - -On the first run, there will be errors because of missing dependencies. Within the terminal showing the errors, run: - -:: +.. code-block:: bash source /opt/ros/humble/setup.bash source install/setup.bash + echo PYTHONPATH=$PYTHONPATH > .env + echo HOME=$HOME >> .env + echo AMENT_PREFIX_PATH=$AMENT_PREFIX_PATH >> .env + echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH >> .env + +In vscode, open user settings and enable the following settings: -Afterwards, press F5 again and the execution should succeed. You can now add breakpoints etc. +.. code-block:: + "python.terminal.activateEnvInCurrentTerminal": true -Creating an Action + +To execute the debug configuration either switch to debug view (on the left) and click on "play" or press F5. + + +Best known Methods ------------------ -- If an action setup() should fail, raise an exception -- Use a state machine, if multiple steps are required -- Implement a ``cleanup`` method to cleanup on scenario end. +Implement an Action +^^^^^^^^^^^^^^^^^^^ + +- If an action's ``setup()`` fails, raise an exception +- Use a state machine, if multiple steps are required +- Implement a ``cleanup()`` method to cleanup on scenario end. +- For debugging/logging: + - Make use of ``self.feedback_message`` + - Make use of ``kwargs['logger']``, available in ``setup()`` + - If you want to draw markers for RViz, use ``kwargs['marker_handler']``, available in ``setup()`` (with ROS backend) diff --git a/docs/dictionary.txt b/docs/dictionary.txt index d9eb061d..8132e16b 100644 --- a/docs/dictionary.txt +++ b/docs/dictionary.txt @@ -4,12 +4,10 @@ devcontainer rviz unmapped Structs -kubernetes submodules namespace enums multiline -amsrl buildkit autoformatter behavior @@ -20,3 +18,11 @@ nav backend osc py +vscode +QoS +dataset +whitespace +odometry +tf +amcl +RViz diff --git a/docs/how_to_run.rst b/docs/how_to_run.rst index 78e2e67b..ba8251a4 100644 --- a/docs/how_to_run.rst +++ b/docs/how_to_run.rst @@ -2,157 +2,6 @@ How to run ========== -With devcontainer ------------------ - -Preparations -~~~~~~~~~~~~ - -If not already installed, install the docker engine on your system -according to the `installation -instructions `__. - -Make sure you follow the `post installation -steps `__. - -To make sure, that the docker daemon is properly set up, run - -.. code-block:: bash - - docker run hello-world - -Install additional packages to build containers (buildkit). - -.. code-block:: bash - - sudo apt install docker-buildx-plugin - -.. note:: - In case you want to use the devcontainer with `Visual Studio - Code `__, you also need to install - docker-compose via - - .. code-block:: bash - - sudo apt update - sudo apt install docker-compose - -Run devcontainer from terminal -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Prior to the actual start of the container, run - -.. code-block:: bash - - xhost + local:ros - -To start the devcontainer without GPU-support, run the following command -*from the root directory of this repository* - -.. code-block:: bash - - bash containers/run_cpu.sh - -To start the devcontainer with GPU-support, run the following command -*from the root directory of this repository* - -.. code-block:: bash - - bash containers/run_gpu.sh - -Inside the devcontainer, you can now safely run and test your -development, e.g., run - -.. code-block:: bash - - ros2 run scenario_execution scenario_execution scenario_execution_base/scenarios/demo_wait_and_log.osc -t - -For a more sophisticated example using a simulated -`Turtlebot4 `__ and -`Nav2 `__, run - -.. code-block:: bash - - ros2 launch scenario_execution_tutorials turtlebot4_simulation_nav2_to_pose_tutorial_launch.py headless:=False - -and you should something like this - -.. figure:: images/tb4_scenario.gif - :alt: turtlebot4 nav2 scenario - - turtlebot4 nav2 scenario - -In case you need an additional terminal inside your running -devcontainer, run - -.. code-block:: bash - - bash containers/exec.sh - -When you are done with testing, please run - -.. code-block:: bash - - xhost - local:ros - -Run devcontainer from Visual Studio Code -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. note:: - Make sure you have installed the necessary VS Code extensions, namely - the `docker extension `__ as - well as the `Dev - Container `__ - extension. - -Open the root folder of this repository in Visual Studio Code -and click the blue item in the lower left corner - -.. figure:: images/graphs/vscode1.png - :alt: vscode1 - - -Afterwards, select “Reopen in Container” in the Selection Window inside -VS Code - -.. figure:: images/graphs/vscode2.png - :alt: vscode2 - - -Now VS Code should open your current working directory inside the -devcontainer. If you now open a terminal inside VS Code, you can run and -test your development safely inside the container, e.g., run - -.. code-block:: bash - - ros2 run scenario_execution scenario_execution scenario_execution_base/scenarios/demo_wait_and_log.osc -t - -For a more sophisticated example using a simulated -`Turtlebot4 `__ and -`Nav2 `__, run - -.. code-block:: bash - - ros2 launch scenario_execution_tutorials turtlebot4_simulation_nav2_to_pose_tutorial_launch.py headless:=False - -and you should something like this - -.. figure:: images/tb4_scenario.gif - :alt: turtlebot4 nav2 scenario - - turtlebot4 nav2 scenario - -Once you are done, you can cancel the remote connection, by again -clicking on the blue item in the lower left corner and select “Close -Remote Connection” - -.. figure:: images/graphs/vscode3.png - :alt: vscode3 - - -With local installation ------------------------ - First, build the packages: .. code-block:: bash @@ -178,3 +27,8 @@ information of py_trees and parser: .. code-block:: bash ros2 run scenario_execution scenario_execution $(PATH_TO_SCENARIO_FILE) -t -d + + +Using RVIZ to trigger scenario +------------------------------ + diff --git a/docs/images/graphs/osc2_namespace.png b/docs/images/graphs/osc2_namespace.png deleted file mode 100644 index 0e3f91b8..00000000 Binary files a/docs/images/graphs/osc2_namespace.png and /dev/null differ diff --git a/docs/images/graphs/scenario_execution_structure.png b/docs/images/graphs/scenario_execution_structure.png deleted file mode 100644 index afa863df..00000000 Binary files a/docs/images/graphs/scenario_execution_structure.png and /dev/null differ diff --git a/docs/images/graphs/scenario_structure.png b/docs/images/graphs/scenario_structure.png deleted file mode 100644 index bdc70cd7..00000000 Binary files a/docs/images/graphs/scenario_structure.png and /dev/null differ diff --git a/docs/images/graphs/vscode1.png b/docs/images/graphs/vscode1.png deleted file mode 100644 index dad68acd..00000000 Binary files a/docs/images/graphs/vscode1.png and /dev/null differ diff --git a/docs/images/graphs/vscode2.png b/docs/images/graphs/vscode2.png deleted file mode 100644 index 383336af..00000000 Binary files a/docs/images/graphs/vscode2.png and /dev/null differ diff --git a/docs/images/graphs/vscode3.png b/docs/images/graphs/vscode3.png deleted file mode 100644 index 28d0575b..00000000 Binary files a/docs/images/graphs/vscode3.png and /dev/null differ diff --git a/docs/images/parsing.png b/docs/images/parsing.png index f04c99bd..12314fbe 100755 Binary files a/docs/images/parsing.png and b/docs/images/parsing.png differ diff --git a/docs/images/scenario_execution_structure.png b/docs/images/scenario_execution_structure.png new file mode 100644 index 00000000..08832c62 Binary files /dev/null and b/docs/images/scenario_execution_structure.png differ diff --git a/docs/images/graphs/tree_example.png b/docs/images/tree_example.png similarity index 100% rename from docs/images/graphs/tree_example.png rename to docs/images/tree_example.png diff --git a/docs/index.rst b/docs/index.rst index e3ef298e..42b86850 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,19 +2,19 @@ Scenario Execution ================== -Scenario Execution for Robotics (SER) is a backend- and middleware-agnostic library, that will enable the robotics community to perform reproducible experiments at scale and allows a seamless transition from simulation to real-world experiments. -SER is written in Python and builds upon the generic scenario description language `OpenScenario2.0 `__ and `pytrees `__. +Scenario Execution for Robotics is a backend- and middleware-agnostic library, that enables the robotics community to perform reproducible experiments at scale and allows a seamless transition from simulation to real-world experiments. +Scenario Execution is written in Python and builds upon the generic scenario description language `OpenScenario2 `__ and `pytrees `__. -SER reads a scenario definition from a file, translates it to a py-trees behavior tree and then executes it. This separation of the scenario definition from the implementation massively reduces the manual efforts of (robotics) scenario creation. -Although SER can be used as a pure Python library, it is mainly targeted to be used with the Robot Operating System (ROS2). The backend-agnostic implementation allows SER to be used with both, robotics simulators such as Gazebo and physical robots, with minimal adaptations necessary in the scenario description file.  +Scenario Execution reads a scenario definition from a file, translates it to a py-trees behavior tree and then executes it. This separation of the scenario definition from the implementation massively reduces the manual efforts of (robotics) scenario creation. +Although Scenario Execution can be used as a pure Python library, it is mainly targeted to be used with the `Robot Operating System (ROS2) `__. The backend-agnostic implementation allows Scenario Execution to be used with both, robotics simulators such as `Gazebo `__ and physical robots, with minimal adaptations necessary in the scenario description file.  To give an impression of the functionality of scenario execution, the following animation shows an example scenario with a turtlebot-like robot in simulation using Nav2 to navigate towards a specified navigation goal in a simulated warehouse environment. Once the robot -reaches a reference position and a box is spawned in front of the robot +reaches a reference position a box is spawned in front of the robot as an unmapped static obstacle that needs to be avoided. Upon arrival of -the goal position, the scenario is ends with success and the simulation +the goal position, the scenario ends and the simulation gets cleaned up. .. figure:: images/scenario.gif @@ -29,7 +29,7 @@ gets cleaned up. setup how_to_run tutorials - actions architecture - openscenario2_support + libraries development + openscenario2 \ No newline at end of file diff --git a/docs/libraries.rst b/docs/libraries.rst new file mode 100644 index 00000000..f188655a --- /dev/null +++ b/docs/libraries.rst @@ -0,0 +1,212 @@ +Libraries +========= + +Beside ``osc.standard`` provided by OpenSCENARIO 2, multiple libraries are provided with scenario execution. + +- ``osc.helpers`` Helpers Library +- ``osc.robotics``: Robotics Library +- ``osc.ros``: ROS Library +- ``osc.gazebo``: Gazebo Library + +Additional features can be implemented by defining your own library. + +``osc.helpers`` +--------------- + +Actions +^^^^^^^ + +``log()`` +""""""""" + +For debugging purposes, log a string using the available log mechanism. + +- ``msg: string``: String to log + +``run_external_process()`` +"""""""""""""""""""""""""" + +Run an external process. Reports `running` while the process has not finished. + +- ``command: string``: Command to execute + + +``osc.robotics`` +---------------- + +Actors +^^^^^^ + +``differential_drive_robot`` +"""""""""""""""""""""""""""" +A differential drive robot actor. + +``osc.ros`` +----------- + +Actions +^^^^^^^ + +``wait_for_data()`` +""""""""""""""""""" + +Wait for a specific data on a ROS topic. + +- ``topic_name: string``: Name of the topic to connect to +- ``topic_type: string``: Class of the message type (e.g. ``std_msgs.msg.String``) +- ``qos_profile: qos_preset_profiles``: QoS Preset Profile for the subscriber (default: ``qos_preset_profiles!system_default``) +- ``clearing_policy: clearing_policy``: When to clear the data (default: ``clearing_policy!on_initialise``) + + +``wait_for_topics()`` +""""""""""""""""""""" + +Wait for topics to get available (i.e. publisher gets available). + +- ``topics: string``: Whitespace-separated list of topics + + +``check_data()`` +"""""""""""""""" +Wait for a topic message, compare a message field against a specific value + +- ``topic_name: string``: Name of the topic to connect to +- ``topic_type: string``: Class of the message type (e.g. ``std_msgs.msg.String``) +- ``qos_profile: qos_preset_profiles``: QoS Preset Profile for the subscriber (default: ``qos_preset_profiles!system_default``) +- ``variable_name: string``: Name of the variable to check +- ``expected_value: string``: Expected value of the variable +- ``comparison_operator: comparison_operator``: The comparison operator to apply (default: ``comparison_operator!eq``) +- ``fail_if_no_data: bool``: py_trees.common.Status.FAILURE instead of py_trees.common.Status.RUNNING if there is no data yet (default: ``false``) +- ``fail_if_bad_comparison: bool``: py_trees.common.Status.FAILURE instead of py_trees.common.Status.RUNNING if comparison failed (default: ``true``) +- ``clearing_policy: clearing_policy``: When to clear the data (default: ``clearing_policy!on_initialise``) + +``service_call()`` +"""""""""""""""""" + +Call a ROS service and wait for the reply. + +- ``service_name: string``: Name of the service to connect to +- ``service_type: string``: Class of the message type (e.g. ``std_srvs.msg.Empty``) +- ``data: string``: Service call content + +``topic_publish()`` +""""""""""""""""""" + +Publish a message on a topic. + +- ``topic_name: string``: Name of the topic to publish to +- ``topic_type: string``: Class of the message type (e.g. ``std_msgs.msg.String``) +- ``qos_profile: qos_preset_profiles``: QoS Preset Profile for the subscriber (default: ``qos_preset_profiles!system_default``) +- ``value: string``: Value to publish + +``set_node_parameter()`` +"""""""""""""""""""""""" + +Set a parameter of a node. + +- ``node_name: string``: Name of the node +- ``parameter_name: string``: Name of the parameter +- ``parameter_value: string``: Value of the parameter + +``record_bag()`` +"""""""""""""""" + +Record a ROS bag. + +- ``destination_dir: string``: The destination for the ROS bag (if empty, the current directory is used) +- ``topics: string``: Whitespace-separated list of topics to capture +- ``timestamp_suffix: bool``: Add a timestamp suffix to output directory name (default: ``true``) +- ``hidden_topics: bool``: Whether to record hidden topics (default: ``false``) +- ``storage: string``: Storage type to use (empty string: use ROS bag record default) + +``log_check()`` +""""""""""""""" +Wait for specific output in ROS log (i.e. `/rosout` topic). + +- ``values: string``: string or list of strings (in python syntax, e.g. "[\'foo\', \'bar\']") + +``differential_drive_robot.init_nav2()`` +"""""""""""""""""""""""""""""""""""""""" + +Initialize nav2. + +- ``initial_pose: pose_3d``: The initial pose to set during initialization +- ``base_frame_id: string``: Base Frame ID (default: ``base_link``) +- ``use_initial_pose: bool``: If false, no initial_pose is needed (useful when using slam instead of amcl for localization) (default: ``true``) +- ``namespace_override: string``: If set, it's used as namespace (instead of the associated actor's namespace) +- ``wait_for_initial_pose: bool``: If true the initial pose needs to be set externally (e.g. manually through rviz)(default: ``false``) + +``differential_drive_robot.nav_to_pose()`` +"""""""""""""""""""""""""""""""""""""""""" +Use nav2 to navigate to goal pose. + +- ``goal_pose: pose_3d``: Goal pose to navigate to +- ``namespace_override: string``: If set, it's used as namespace (instead of the associated actor's namespace) +- ``action_topic: string``: Action name (default: ``navigate_to_pose``) +- ``monitor_progress: bool``: If yes, the action returns after the goal is reached or on failure. If no, the action returns after request. (default: ``true``) + +``differential_drive_robot.nav_through_poses()`` +"""""""""""""""""""""""""""""""""""""""""""""""" + +Use nav2 to navigate through poses. + +- ``goal_pose: string``: Goal poses to navigate to (format: ``x1,y1,yaw1;x2,y2,yaw2;...``) +- ``namespace_override: string``: If set, it's used as namespace (instead of the associated actor's namespace) +- ``monitor_progress: bool``: If yes, the action returns after the goal is reached or on failure. If no, the action returns after request. (default: ``true``) + +``differential_drive_robot.tf_close_to()`` +"""""""""""""""""""""""""""""""""""""""""" + +Wait until a TF frame is close to a defined reference point. + +- ``namespace_override: string``: if set, it's used as namespace (instead of the associated actor's namespace) +- ``reference_point: position_3d``: Reference point to measure to distance to (z is not considered) +- ``threshold: length``: Distance at which the action succeeds. +- ``sim: bool``: In simulation, we need to look up the transform map --> base_link at a different time as the scenario execution node is not allowed to use the sim time (default: ``false``) +- ``robot_frame_id: string``: Defines the TF frame id of the robot (default: ``base_link``) + +``differential_drive_robot.odometry_distance_traveled()`` +""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Wait until a defined distance was traveled, based on odometry. + +- ``namespace: string``: Namespace of the odometry topic +- ``distance: length``: Traveled distance at which the action succeeds. + + +``osc.gazebo`` +-------------- + +Actions +^^^^^^^ + + +``wait_for_sim()`` +"""""""""""""""""" +Wait for simulation to become active (checks for simulation clock). + +- ``world_name: string``: Gazebo world name (default: ``default``) + +``actor_exists()`` +"""""""""""""""""" + +Waits for an actor to exist within simulation. + +- ``entity_name``: Entity name within simulation +- ``world_name: string``: Gazebo world name (default: ``default``) + +``osc_object.spawn()`` +"""""""""""""""""""""" + +Spawn an actor within simulation by using the ``model`` and ``namespace`` of the associated actor. + +- ``spawn_pose: pose_3d``: Pose of the spawned actor. +- ``world_name: string``: Gazebo world name (default: ``default``) +- ``xacro_arguments: string``: Comma-separated list of argument key:=value pairs + +``osc_object.delete()`` +""""""""""""""""""""""" + +Delete an object from the simulation. + +- ``world_name: string``: Gazebo world name (default: ``default``) diff --git a/docs/openscenario2.rst b/docs/openscenario2.rst new file mode 100644 index 00000000..83c3ce89 --- /dev/null +++ b/docs/openscenario2.rst @@ -0,0 +1,99 @@ +OpenSCENARIO 2 +============== + +General +------- + +This tool supports a subset of the `OpenSCENARIO +2 `__ standard. + +The official documentation is available +`here `__. + +The `standard library of +OSC2 `__ +was adapted to be usable by the current parsing support of scenario execution. + +In the following the supported features are described. + +.. role:: raw-html(raw) + :format: html + +Supported features +------------------ + +In the following the OpenSCENARIO 2 keywords are listed with their current support status. + + +======================= ==================== ============================= +Element Tag Support Notes +======================= ==================== ============================= +``action`` :raw-html:`✅` partially, see details below +``actor`` :raw-html:`✅` partially, see details below +``as`` :raw-html:`❌` +``bool`` :raw-html:`✅` +``call`` :raw-html:`❌` +``cover`` :raw-html:`❌` +``def`` :raw-html:`❌` +``default`` :raw-html:`❌` +``do`` :raw-html:`✅` +``elapsed`` :raw-html:`✅` +``emit`` :raw-html:`✅` +``enum`` :raw-html:`✅` +``event`` :raw-html:`✅` +``every`` :raw-html:`❌` +``expression`` :raw-html:`❌` +``extend`` :raw-html:`❌` +``external`` :raw-html:`❌` +``fall`` :raw-html:`❌` +``float`` :raw-html:`✅` +``global`` :raw-html:`✅` +``hard`` :raw-html:`❌` +``if`` :raw-html:`❌` +``import`` :raw-html:`✅` +``inherits`` :raw-html:`✅` +``int`` :raw-html:`✅` +``is`` :raw-html:`❌` +``it`` :raw-html:`✅` +``keep`` :raw-html:`✅` +``list`` :raw-html:`✅` +``of`` :raw-html:`✅` +``on`` :raw-html:`❌` +``one_of`` :raw-html:`✅` +``only`` :raw-html:`❌` +``parallel`` :raw-html:`✅` +``range`` :raw-html:`❌` +``record`` :raw-html:`❌` +``remove_default`` :raw-html:`❌` +``rise`` :raw-html:`❌` +``scenario`` :raw-html:`✅` +``serial`` :raw-html:`✅` +``SI`` :raw-html:`✅` +``string`` :raw-html:`✅` +``struct`` :raw-html:`✅` +``type`` :raw-html:`✅` +``uint`` :raw-html:`✅` +``undefined`` :raw-html:`❌` +``unit`` :raw-html:`✅` +``until`` :raw-html:`❌` +``var`` :raw-html:`✅` +``wait`` :raw-html:`✅` +``with`` :raw-html:`✅` +======================= ==================== ============================= + + +Composition Types +^^^^^^^^^^^^^^^^^ + +Composition types are ``struct``, ``actor``, ``action``, ``scenario``. + +============== ==================== ========= +Element Type Support Notes +============== ==================== ========= +Event :raw-html:`✅` +Field :raw-html:`✅` +Constraint :raw-html:`✅` partially +Method :raw-html:`❌` +Coverage :raw-html:`❌` +Modifier :raw-html:`❌` +============== ==================== ========= diff --git a/docs/openscenario2_support.rst b/docs/openscenario2_support.rst deleted file mode 100644 index 15aeb7e1..00000000 --- a/docs/openscenario2_support.rst +++ /dev/null @@ -1,309 +0,0 @@ -OpenSCENARIO2 Support ---------------------- - -This tool supports a subset of the `OpenSCENARIO -2 `__ PRC -standard. In the following the supported features are described. - -In addition, it is recommended to take a look into the official -documentation available -`here `__. - -The `standard library of -OSC2 `__ -was slightly adapted as py-osc2 is not yet support multiline enum -definitions. - -Level of support -~~~~~~~~~~~~~~~~ - -In the following the OpenSCENARIO2 standard library elements are listed -with their current support status. - -Scalar types and units -^^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -.. raw:: html - - - -========================================= ======= ===== -Element Tag Support Notes -========================================= ======= ===== -``library-physical-length`` ✅ -``library-physical-time`` ✅ -``library-physical-acceleration`` ✅ -``library-physical-jerk`` ✅ -``library-physical-angle`` ✅ -``library-physical-speed`` ✅ -``library-physical-angular_rate`` ✅ -``library-physical-angular_acceleration`` ✅ -``library-physical-mass`` ✅ -``library-physical-temperature`` ✅ -``library-physical-pressure`` ✅ -``library-physical-luminous_intensity`` ✅ -``library-physical-luminous_flux`` ✅ -``library-physical-illuminance`` ✅ -``library-physical-electrical_current`` ✅ -``library-physical-amount_of_substance`` ✅ -========================================= ======= ===== - -Structs -^^^^^^^ - -========================================= ======= ======== -Element Tag Support Notes -========================================= ======= ======== -``library-position_3d`` ✅ -``library-celestial_position_2d`` ✅ -``library-geodetic_position_2d`` ✅ -``library-orientation_3d`` ✅ -``library-pose_3d`` ✅ -``library-translational_velocity_3d`` ✅ -``library-orientation_rate_3d`` ✅ -``library-velocity_6d`` ✅ -``library-translational_acceleration_3d`` ✅ -``library-orientation_acceleration_3d`` ✅ -``library-acceleration_6d`` ✅ -``axle`` ✅ -``bounding_box`` ✅ -``crossing_type`` ✅ -``route_point`` ✅ -``xyz_point`` ✅ -``odr_point`` ✅ -``library-path`` ✅ modified -``relative_path`` ✅ -``relative_path_pose_3d`` ✅ -``relative_path_st`` ✅ -``relative_path_odr`` ✅ -``trajectory`` ✅ -``relative_trajectory`` ✅ -``relative_trajectory_pose_3d`` ✅ -``relative_trajectory_st`` ✅ -``relative_trajectory_odr`` ✅ -``any_shape`` ✅ -``any_acceleration_shape`` ✅ -``any_speed_shape`` ✅ -``any_position_shape`` ✅ -``any_lateral_shape`` ✅ -``common_acceleration_shape`` ✅ -``common_speed_shape`` ✅ -``common_position_shape`` ✅ -``common_lateral_shape`` ✅ -``behavioral_model`` ✅ -``bm_engine`` ✅ -========================================= ======= ======== - -Enums -^^^^^ - -=========================== ======= ======== -Element Tag Support Notes -=========================== ======= ======== -``color`` ✅ -``intended_infrastructure`` ✅ -``vehicle_category`` ✅ -``driving_rule`` ✅ -``directionality`` ✅ -``lane_type`` ✅ -``lane_use`` ✅ -``side_left_right`` ✅ -``crossing_marking`` ✅ modified -``crossing_use`` ✅ -``crossing_elevation`` ✅ -``junction_direction`` ✅ -``route_overlap_kind`` ✅ -``lateral_overlap_kind`` ✅ -``dynamic_profile`` ✅ -``lane_change_side`` ✅ -``gap_direction`` ✅ -``headway_direction`` ✅ -``lat_measure_by`` ✅ -``yaw_measure_by`` ✅ -``orientation_measured_by`` ✅ -``movement_options`` ✅ -``connect_route_points`` ✅ modified -``path_interpolation`` ✅ -``at`` ✅ -``movement_mode`` ✅ -``track`` ✅ -``distance_direction`` ✅ -``distance_mode`` ✅ -``relative_transform`` ✅ -``on_route_type`` ✅ -``route_distance_enum`` ✅ -=========================== ======= ======== - -Actor -^^^^^ - -======================= ======= ======== -Element Tag Support Notes -======================= ======= ======== -``osc_actor`` ✅ -``physical_object`` ✅ modified -``stationary_object`` ✅ -``movable_object`` ✅ modified -``traffic_participant`` ✅ -``vehicle`` ✅ -``person`` ✅ -``animal`` ✅ -======================= ======= ======== - -Environment (An Actor) -^^^^^^^^^^^^^^^^^^^^^^ - -========================== ======= ======== -Element Tag Support Notes -========================== ======= ======== -``environment`` ✅ modified -``weather`` ✅ -``air`` ✅ -``precipitation`` ✅ -``wind`` ✅ -``fog`` ✅ -``clouds`` ✅ -``celestial_light_source`` ✅ -========================== ======= ======== - -Map (An Actor) -^^^^^^^^^^^^^^ - -================== ======= ===== -Element Tag Support Notes -================== ======= ===== -``map`` ✅ -``route`` ✅ -``route_element`` ✅ -``road`` ✅ -``lane_section`` ✅ -``lane`` ✅ -``crossing`` ✅ -``junction`` ✅ -``compound_route`` ✅ -``compound_lane`` ✅ -================== ======= ===== - -Action - movable object -^^^^^^^^^^^^^^^^^^^^^^^ - -============================================ ======= ===== -Element Tag Support Notes -============================================ ======= ===== -``osc_actor.osc_action`` ✅ -``movable_object.action_for_movable_object`` ✅ -``movable_object.move`` ✅ -``movable_object.remain_stationary`` ✅ -``movable_object.assign_position`` ✅ -``movable_object.assign_orientation`` ✅ -``movable_object.assign_speed`` ✅ -``movable_object.assign_acceleration`` ✅ -``movable_object.replay_path`` ✅ -``movable_object.replay_trajectory`` ✅ -``movable_object.change_position`` ✅ -``movable_object.change_speed`` ✅ -``movable_object.keep_speed`` ✅ -``movable_object.change_acceleration`` ✅ -``movable_object.keep_acceleration`` ✅ -``movable_object.follow_path`` ✅ -``movable_object.follow_trajectory`` ✅ -============================================ ======= ===== - -Action - vehicle -^^^^^^^^^^^^^^^^ - -=============================== ======= ===== -Element Tag Support Notes -=============================== ======= ===== -``vehicle.action_for_vehicle`` ✅ -``vehicle.drive`` ✅ -``vehicle.follow_lane`` ✅ -``vehicle.change_lane`` ✅ -``vehicle.change_space_gap`` ✅ -``vehicle.keep_space_gap`` ✅ -``vehicle.change_time_headway`` ✅ -``vehicle.keep_time_headway`` ✅ -=============================== ======= ===== - -Action - person -^^^^^^^^^^^^^^^ - -============================ ======= ===== -Element Tag Support Notes -============================ ======= ===== -``person.action_for_person`` ✅ -``person.walk`` ✅ -============================ ======= ===== - -Action - environment -^^^^^^^^^^^^^^^^^^^^ - -========================================= ======= ===== -Element Tag Support Notes -========================================= ======= ===== -``environment.action_for_environment`` ✅ -``environment.air`` ✅ -``environment.rain`` ✅ -``environment.snow`` ✅ -``environment.wind`` ✅ -``environment.fog`` ✅ -``environment.cloud`` ✅ -``environment.assign_celestial_position`` ✅ -========================================= ======= ===== - -Modifier - location based modifiers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -============================== ======= ===== -Element Tag Support Notes -============================== ======= ===== -``position`` ❌ -``distance`` ❌ -``lane`` ❌ -``keep_lane`` ❌ -``lateral`` ❌ -``yaw`` ❌ -``orientation`` ❌ -``along`` ❌ -``keep_position`` ❌ -``along_trajectory`` ❌ -``stationary_object.location`` ❌ -============================== ======= ===== - -Modifier - rate of change based modifiers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -===================== ======= ===== -Element Tag Support Notes -===================== ======= ===== -``speed`` ❌ -``acceleration`` ❌ -``keep_speed`` ❌ -``change_speed`` ❌ -``physical_movement`` ❌ -``avoid_collisions`` ❌ -``change_lane`` ❌ -===================== ======= ===== - -Modifier - map based modifiers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -================================ ======= ===== -Element Tag Support Notes -================================ ======= ===== -``map.number_of_lanes`` ❌ -``map.routes_are_in_sequence`` ❌ -``map.roads_follow_in_junction`` ❌ -``map.routes_overlap`` ❌ -``map.lane_side`` ❌ -``map.compound_lane_side`` ❌ -``map.end_lane`` ❌ -``map.start_lane`` ❌ -``map.crossing_connects`` ❌ -``map.routes_are_opposite`` ❌ -``map.set_map_file`` ❌ -================================ ======= ===== diff --git a/docs/tutorials.rst b/docs/tutorials.rst index 61fe09f3..e6e6567b 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -1,349 +1,220 @@ Tutorials ========= -Table of contents ------------------ - -- `Create a scenario in - OpenScenario2 <#create-scenario-in-openscenario-v200>`__ -- `Create a custom action plugin <#create-action-plugin>`__ -- `Create a scenario with a simulated Turtlebot4 and - Nav2 <#create-a-scenario-with-a-simulated-turtlebot4-and-nav2>`__ -- `Create a scenario with a simulated Turtlebot4 and Nav2 and spawning - a static unmapped - obstacle <#create-a-scenario-with-a-simulated-turtlebot4-and-nav2-and-spawning-a-static-unmapped-obstacle>`__ - -Create Scenario in OpenSCENARIO V2.0.0 --------------------------------------- - -To create a scenario in OpenSCENARIO V2.0.0 syntax, first create a file +Code for all tutorials is available in :repo_link:`examples`. + +Define and Execute Scenario +--------------------------- + +To create a scenario in OpenSCENARIO 2 syntax, first create a file with the extension ``.osc``. Input the following code in the file. -:: +.. code-block:: # import the libraries with import expression - import osc.builtin - - # Comments start with "#" - # This is a comment. + import osc.standard + import osc.helpers # declare the scenario by the syntax: "scenario scenario_name:" - scenario create_scenario_tutorial: + scenario example_ros_topic: # define the content of the scenario with "do_directive" - do serial: - # log a message on the screen with "log" action from the built-in library - log() with: - # constrain the logged msg to "Hello World!" - keep(it.msg == "Hello World") - # emit the "end" event to signalize the end of the scenario - emit end - -The first line ``import osc.builtin`` will import the integrated library -``builtin.osc`` from the package “scenario_execution_base”. All the -OpenSCENARIO 2 types defined in the file will be imported into the -parser. - -The comments in OpenSCENARIO 2 always start with hashtag “#”. - -Then, declare a scenario without an associated actor. The term -“scenario” declares that this is a scenario and -“create_scenario_tutorial” is the name of the scenario followed by a -colon. The content of the scenario is defined in the “do_directive”. The -term ``do`` declares that this is a “do_directive”. The term ``serial`` -states that the following layer will be executed in sequence. Use the -``log`` action defined in the built-in library to log a message on the -screen. The term ``with`` declares that there are behavior invocation -members that modify the behavior. In the behavior invocation members, a -“keep constraint” is given to constrain the action ``log``. In side the -“keep constraint”, the ``it`` term references to the current namespace. -In this case, it’s the ``log`` action. With ``.`` symbol, you can access -the members of the ``log`` action. The ``it.msg`` references to the -parameter ``msg`` of the action ``log``. The relational operator ``==`` -signalizes that the message should be equal to the string -``"Hello World!"``. After the ``log`` action is invoked, the scenario -should emit an ``end`` event to tell the scenario execution to shut down -and return success on scenario “create_scenario_tutorial”. On the other -hand, if you want to define a condition where the scenario fails, use -``emit fail`` to shut down the scenario execution and return failure. + do serial: # execute children one after the other + log("Hello World!") # log a message on the screen with "log" action from the built-in library + wait elapsed(3s) # wait three seconds + log("Good Bye!") # log another message + +The first two lines ``import osc.standard`` and ``import osc.helpers`` will import the named libraries that provide required definitions. In this example ``helpers`` library provides the ``log`` action and ``standard`` provides the definition of the `s` unit to specify seconds. + +.. note:: + Comments in OpenSCENARIO 2 always start with ``#``. + +Then, a scenario with the name ``hello_world`` get declared. The following colon states that all following and indented lines +are part of it. The single top-level action of the scenario is defined in the ``do`` directive. +The term ``serial`` states that the included actions will be executed in sequence. + +.. note:: + OpenSCENARIO 2 supports the following compositions: + + * ``parallel``: execute actions in parallel, continue afterwards + * ``serial``: execute actions, one after the other + * ``one_of``: execute actions in parallel, continue after one finished + +Use the ``log`` action, defined within the imported library, to log a message ``Hello World!`` on the +screen. After the ``log`` action is invoked, the ``wait`` directive makes the scenario execution to wait for 3 seconds. Afterwards another ``log`` action is triggered and the scenario ends afterwards. + +.. note:: + Scenario execution uses the predefined events ``end`` and ``fail`` to detect success or failure of a scenario. If no ``emit end`` or ``emit fail`` is defined, a success is assumed. + +.. note:: + It is good practice to define a timeout action in parallel to the expected actions within a scenario. + + .. code-block:: + + scenario example: + do parallel: + serial: + ... + serial: + wait elapsed(60s) + emit fail Use this code to see a launch of this tutorial: .. code-block:: bash - colcon build --packages-up-to scenario_execution_tutorials && source install/setup.bash \ - && ros2 launch scenario_execution_tutorials scenario_execution_create_scenario_tutorial_launch.py + colcon build --packages-up-to scenario_execution && source install/setup.bash \ + && ros2 launch scenario_execution scenario_launch.py scenario:=examples/example_scenario/hello_world.osc -Create Action Plugin --------------------- +.. _scenario_library: -To show how to create a action plugin for the scenario execution, we -will use the ``log`` action as an example. +Create Scenario Library +----------------------- -First, we need to define the ``tutorial_log`` action in OpenSCENARIO 2. +To add new features to scenario execution, extensions libraries can be created. An extension library typically provides one or more +OpenSCENARIO 2 definition files and might additionally provide action implementations. -:: +To show how to create such a library for scenario execution, we will add a ``custom_action`` action as an example. - action tutorial_log: - plugin: string = "TutorialLog" - msg: string +First, we need to define the ``custom_action`` in a OpenSCENARIO 2 file. -The ``plugin`` parameter shows what is the name of the entry point of -the plugin. The ``msg`` parameter is the message we want to log to the -screen. +.. code-block:: -Then, we can write our code for action plugin in Python. + action custom_action: + data: string -:: +The ``data`` parameter is used to pass the data of type string to the action plugin implementation. + +Then, we can write the implementation of action plugin in Python. + +.. code-block:: import py_trees - from scenario_execution_base.osc2_utils.osc2_type import ActionPlugin - # Step 1: define the py_trees behavior - class LogAction(py_trees.behaviour.Behaviour): + # define the py_trees behavior + class CustomAction(py_trees.behaviour.Behaviour): # Override the __init__ function to accept parsed arguments. - def __init__(self, msg: str): - super().__init__("Log") - self.msg = msg + def __init__(self, name, data: str): + super().__init__(name) + self.data = data # Override the update function to define how the behavior is ticking. def update(self): - print(self.msg) + print(f"Custom Action Triggered. Data: {self.data}") return py_trees.common.Status.SUCCESS - # Step 2: create the action plugin by inherit from the class "ActionPlugin" - class Log(ActionPlugin): - # Override the init function to give the action plugin a name - def __init__(self, action_handle) -> None: - super().__init__('Log', action_handle) - - # override the create_behavior function to return the behavior we just declared. - def create_behavior(self) -> py_trees.behaviour.Behaviour: - return LogAction - - # override the parse function to parse the parameter from the osc2 action - def parse(self): - """ - Parse the parameters from the action - """ - self.kwargs['msg'] = self.action_handle.parameters['msg'].value - -In the example, we created an action plugin to print a message on the -screen. The first step is to create a py_trees behavior for the action + +In the example, we created a custom action plugin to print a message on the +screen. The first step is to create a ``py_trees`` behavior for the action plugin. First, override the ``__init__()`` function to accept the parsed -parameter from the action plugin. The action plugin ``Log`` only parses -one parameter ``msg``, so the behavior only has to accept ``msg`` as an +parameter from the action plugin. Beside the fixed parameter ``name`` all parameters defined within the OpenSCENARIO 2 file +are handed over to `__init__`. +The action plugin ``custom_action`` only defines one parameter ``data``, so the behavior only has to accept ``data`` as an argument. Then, override the ``update()`` function to define how the -behavior works. In this case, the behavior print the message on the screen -and then return success. - -The second step is to create the action plugin. All action plugins need -to inherit from the class ``ActionPlugin`` from -“scenario_execution_base” package. Then, override the -``create_behavior()`` function to return the py_trees behavior we want -to use in our behavior tree. In this case, we return the ``LogAction`` -behavior we just created. Remember to only return the class of the -py_trees behavior, not the instance of the class, because the class -needs to instantiated later when all arguments are parsed. Then, we -override the ``parse()`` function. We define the arguments in the -``self.kwargs`` dictionary, which will be passed to the py_trees -behavior in the ``create_behavior()`` function. In the action plugin, we -have a handle on the ``Action`` object from the parser. We can access -the message parameter through -``self.action_handle.parameters['msg'].value``. - -After we wrote the action plugin, we need to add it to the -“scenario_execution.action_plugins” entry points, so that the parser can +behavior works. In this case, the behavior prints the message on the screen +and then returns success. Please refer to the ``py_trees`` `documentation `_ for details. + +After we wrote the library, we need to add it to the +``scenario_execution.actions`` and ``scenario_execution.osc_libraries`` entry points, so that the parser can find it. -Open up the setup file for your Python package and add this line to the -entry points. +Open up the setup file for your Python package ``setup.py`` and add these lines to the +entry_points section. -:: +.. code-block:: - 'scenario_execution.action_plugins': [ - 'TutorialLog = scenario_execution_tutorial.tutorial_log:Log', + entry_points={ + 'scenario_execution.actions': [ + 'custom_action = example_library.custom_action:CustomAction', ], + 'scenario_execution.osc_libraries': [ + 'example = example_library.get_osc_library:get_example_library', + ] + } -The setup file should look like this afterwards: +To ship the osc library, a ``MANIFEST.in`` must be created and ``include_package_data=True`` must be enabled within ``setup.py``. -:: +Now, you can use the library and the action ``custom_action`` within your scenarios: - from setuptools import setup - - package_name = 'scenario_execution_tutorials' - - setup( - name=package_name, - version='0.0.0', - packages=[package_name], - data_files=[ - ('share/ament_index/resource_index/packages', - ['resource/' + package_name]), - ('share/' + package_name, ['package.xml']), - ], - install_requires=['setuptools'], - zip_safe=True, - maintainer='todo', - maintainer_email='todo@todo.com', - description='TODO: Package description', - license='TODO: License declaration', - tests_require=['pytest'], - entry_points={ - 'console_scripts': [ - ], - 'scenario_execution.action_plugins': [ - 'TutorialLog = scenario_execution_tutorials.tutorial_log:Log', - ], - }, - ) - -Now, you can use your action plugin ``tutorial_log`` in your scenarios: - -:: +.. code-block:: - action tutorial_log: - plugin: string = "TutorialLog" - msg: string + import osc.example - scenario create_action_plugin_tutorial: - do serial: - tutorial_log() with: - keep(it.msg == 'Action Plugin Tutorial') - emit end + scenario example_library: + do serial: + custom_action(data: 'foo') + emit end Use this code to see a launch of this tutorial: .. code-block:: bash - colcon build --packages-up-to scenario_execution_tutorials && source install/setup.bash \ - && ros2 launch scenario_execution_tutorials scenario_execution_create_action_plugin_tutorial_launch.py + colcon build --packages-up-to example_library && source install/setup.bash \ + && ros2 launch scenario_execution scenario_launch.py scenario:=examples/example_library/scenarios/example_library.osc -Create a scenario with a simulated Turtlebot4 and Nav2 ------------------------------------------------------- +Create Navigation Scenario +-------------------------- A simple example scenario for spawning a simulated Turtlebot4 in Gazebo -and control it with Nav2, can be found in :repo_link:`sscenario_execution_tutorials/scenarios/nav2_simulation_nav_to_pose_tutorial.osc`. +and control it with Nav2, can be found in :repo_link:`examples/example_nav2/example_nav2.osc`. This scenario files looks as follows: :: - import osc.ros # imports - import osc.gazebo - - scenario nav2_simulation_nav_to_pose: # scenario name - turtlebot4: differential_drive_robot with: # define turtlebot4 robot - keep(it.namespace == '') - keep(it.model == 'topic:///robot_description') - do parallel: - test_drive: serial: - wait_for_sim() with: # wait for the simulation to start - keep(it.world_name == 'maze') - turtlebot4.spawn() with: # spawn the robot - keep(it.spawn_pose.position.x == 0.0m) - keep(it.spawn_pose.position.y == 0.0m) - keep(it.spawn_pose.position.z == 0.1m) - keep(it.spawn_pose.orientation.yaw == 0.0rad) - keep(it.world_name == 'maze') - turtlebot4.init_nav2() with: # initialize Nav2 - keep(it.initial_pose.position.x == 0.0m) - keep(it.initial_pose.position.y == 0.0m) - keep(it.initial_pose.orientation.yaw == 0.0rad) - turtlebot4.nav_to_pose() with: # navigate to the first goal pose - keep(it.goal_pose.position.x == 3.0m) - keep(it.goal_pose.position.y == -3.0m) - turtlebot4.nav_to_pose() with: # navigate back to spawning position - keep(it.goal_pose.position.x == 0.0m) - keep(it.goal_pose.position.y == 0.0m) - emit end # end the scenario with success - time_out: serial: # timeout to stop scenario after 4 minutes and mark it as failed in case something goes wrong - wait elapsed(240s) - emit fail + import osc.ros + + scenario nav2_simulation_nav_to_pose: + turtlebot4: differential_drive_robot + do parallel: + test_drive: serial: + turtlebot4.init_nav2(pose_3d(position_3d(x: 0.0m, y: 0.0m))) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 3.0m, y: -3.0m))) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 0.0m, y: 0.0m))) + emit end + time_out: serial: + wait elapsed(120s) + emit fail Let’s break down the individual components of the scenario. The -following snippet defines the turtlebot4 amr-object and where the -object’s description comes from (in this case, the robot_description -ROS2 topic). +following snippet defines the turtlebot4 amr-object. -:: +.. code-block:: - turtlebot4: differential_drive_robot with: # define turtlebot4 robot - keep(it.namespace == '') - keep(it.model == 'topic:///robot_description') + turtlebot4: differential_drive_robot: # define turtlebot4 robot The ``do parallel`` runs the actual test drive and a time-out in parallel. In case something goes wrong, the time-out prevents the -scenario from running indefinitely by canceling it after 4 minutes and +scenario from running indefinitely by canceling it after 2 minutes and marking it as failed. -The following snippet make the scenario execution module wait until the -simulation is up and running. Note, that we need to give the name of the -simulation (here, Ignition) as well as the name of the simulation world -(here, maze) to this action. - -:: - - wait_for_sim() with: # wait for the simulation to start - keep(it.world_name == 'maze') - -The following snippet spawns the robot at the origin of the world/map - -:: - - turtlebot4.spawn() with: # spawn the robot - keep(it.spawn_pose.position.x == 0.0m) - keep(it.spawn_pose.position.y == 0.0m) - keep(it.spawn_pose.position.z == 0.1m) - keep(it.spawn_pose.orientation.yaw == 0.0rad) - keep(it.world_name == 'maze') Before being able to navigate, nav2 needs to be initialized. This includes setting the initial pose of the Nav2 localization module `AMCL `__. -:: - - turtlebot4.init_nav2() with: # initialize Nav2 - keep(it.initial_pose.position.x == 0.0m) - keep(it.initial_pose.position.y == 0.0m) - keep(it.initial_pose.orientation.yaw == 0.0rad) +.. code-block:: -In case you want to localize your robot with -`SLAM `__ instead of -AMCL, there is no initial pose needed, i.e., you need to change the -``init_nav2`` action to - -:: - - turtlebot4.init_nav2() with: # initialize Nav2 - keep(it.initial_pose.position.x == 0.0m) - keep(it.initial_pose.position.y == 0.0m) - keep(it.initial_pose.orientation.yaw == 0.0rad) - keep(it.use_initial_pose == false) # the initial pose is not needed if we are using slam + turtlebot4.init_nav2(pose_3d(position_3d(x: 0.0m, y: 0.0m))) # initialize Nav2 Finally, the following snippet calls the Nav2 `NavigateToPose action `__ to make the robot navigate to a specified goal pose and back to the starting position -:: +.. code-block:: - turtlebot4.nav_to_pose() with: # navigate to the first goal pose - keep(it.goal_pose.position.x == 3.0m) - keep(it.goal_pose.position.y == -3.0m) - turtlebot4.nav_to_pose() with: # navigate back to spawning position - keep(it.goal_pose.position.x == 0.0m) - keep(it.goal_pose.position.y == 0.0m) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 3.0m, y: -3.0m))) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 0.0m, y: 0.0m))) Once the robot reached the final goal pose ``emit end`` finishes the scenario and marks it as successful. To try this example, run -:: +.. code-block:: bash - ros2 launch scenario_execution_tutorials turtlebot4_simulation_nav2_to_pose_tutorial_launch.py headless:=False + ros2 launch tb4_sim_scenario sim_nav_scenario_launch.py scenario:=examples/example_nav2/example_nav2.osc headless:=False and you should see something like this @@ -355,9 +226,9 @@ and you should see something like this In case you want to run the navigation with SLAM instead of AMCL, update the scenario file as described above and then run -:: +.. code-block:: bash - ros2 launch scenario_execution_tutorials turtlebot4_simulation_nav2_to_pose_tutorial_launch.py headless:=False slam:=True + ros2 launch tb4_sim_scenario sim_nav_scenario_launch.py scenario:=examples/example_nav2/example_nav2.osc headless:=False slam:=True and you should see something like this @@ -366,13 +237,13 @@ and you should see something like this Turtlebot4 NAV2 scenario SLAM -Create a scenario with a simulated Turtlebot4 and Nav2 and spawning a static unmapped obstacle ----------------------------------------------------------------------------------------------- +Create Navigation Scenario with Obstacle +---------------------------------------- -In this section, we’ll extend the previous example and use the :repo_link:`scenario_execution/action_plugins/tf_close_to.py`. +In this section, we’ll extend the previous example and use the :repo_link:`scenario_execution/actions/tf_close_to.py`. to spawn a static obstacle in front of the robot once it reaches a user-specified reference point. The corresponding scenario can be found -in :repo_link:`scenario_execution_tutorials/scenarios/nav2_simulation_nav_to_pose_object_spawn_tutorial.osc + + + 0 0 0.5 0 0 0 + true + + + 0.5 + + + 0.083 + 0.0 + 0.0 + 0.083 + 0.0 + 0.083 + + + + + + 0.25 0.25 0.25 + + + + + + + 0.25 0.25 0.25 + + + + + + \ No newline at end of file diff --git a/examples/example_simulation/package.xml b/examples/example_simulation/package.xml new file mode 100644 index 00000000..3e032f01 --- /dev/null +++ b/examples/example_simulation/package.xml @@ -0,0 +1,25 @@ + + + + example_simulation + 1.0.0 + Scenario Execution Example for Simulation + Intel Labs + Intel Labs + Apache-2.0 + + scenario_execution + scenario_execution_gazebo + tb4_sim_scenario + + rclpy + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/examples/example_simulation/resource/example_simulation b/examples/example_simulation/resource/example_simulation new file mode 100644 index 00000000..e69de29b diff --git a/examples/example_simulation/scenarios/example_simulation.osc b/examples/example_simulation/scenarios/example_simulation.osc new file mode 100644 index 00000000..0560f7e6 --- /dev/null +++ b/examples/example_simulation/scenarios/example_simulation.osc @@ -0,0 +1,28 @@ +import osc.ros +import osc.gazebo + +scenario nav2_simulation_nav_to_pose: + turtlebot4: differential_drive_robot + box: osc_object + do parallel: + test_drive: serial: + turtlebot4.init_nav2(pose_3d(position_3d(x: 0.0m, y: 0.0m))) + parallel: + serial: + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 3.0m, y: -3.0m))) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 0.0m, y: 0.0m))) + serial: + turtlebot4.tf_close_to( + reference_point: position_3d(x: 1.5m, y: -1.5m), + threshold: 0.4m, + robot_frame_id: 'turtlebot4_base_link_gt') + box.spawn( + spawn_pose: pose_3d( + position: position_3d(x: 2.0m, y: -2.0m, z: 0.1m), + orientation: orientation_3d(yaw: 0.0rad)), + model: 'example_simulation://models/box.sdf', + world_name: 'maze') + emit end + time_out: serial: + wait elapsed(120s) + emit fail diff --git a/examples/example_simulation/setup.py b/examples/example_simulation/setup.py new file mode 100644 index 00000000..246fa421 --- /dev/null +++ b/examples/example_simulation/setup.py @@ -0,0 +1,43 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from glob import glob +import os +from setuptools import setup + +PACKAGE_NAME = 'example_simulation' + +setup( + name=PACKAGE_NAME, + version='1.0.0', + packages=[PACKAGE_NAME], + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + PACKAGE_NAME]), + ('share/' + PACKAGE_NAME, ['package.xml']), + (os.path.join('share', PACKAGE_NAME, 'scenarios'), glob('scenarios/*.osc')), + (os.path.join('share', PACKAGE_NAME, 'models'), glob('models/*.sdf')), + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Scenario Execution Example for Simulation', + license='Apache License 2.0', + tests_require=['pytest'], + entry_points={ + }, +) diff --git a/requirements.txt b/requirements.txt index 8d98e164..b8ad65fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ antlr4-python3-runtime==4.7.2 transforms3d==0.3.1 pexpect==4.9.0 -kubernetes==29.0.0 defusedxml==0.7.1 diff --git a/scenario_coverage/README.md b/scenario_coverage/README.md new file mode 100644 index 00000000..1bb44f5e --- /dev/null +++ b/scenario_coverage/README.md @@ -0,0 +1,6 @@ +# Scenario Coverage + +The `scenario_coverage` packages provides two tools: + +- `scenario_variation`: Create concrete scenarios out of scenario with variation definition +- `scenario_batch_execution`: Execute multiple scenarios, one after the other. \ No newline at end of file diff --git a/scenario_coverage/package.xml b/scenario_coverage/package.xml new file mode 100644 index 00000000..07454065 --- /dev/null +++ b/scenario_coverage/package.xml @@ -0,0 +1,21 @@ + + + + scenario_coverage + 1.0.0 + Robotics Scenario Execution Coverage Tools + Intel Labs + Intel Labs + Apache-2.0 + + scenario_execution_base + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/scenario_coverage/resource/scenario_coverage b/scenario_coverage/resource/scenario_coverage new file mode 100644 index 00000000..e69de29b diff --git a/scenario_coverage/scenario_coverage/__init__.py b/scenario_coverage/scenario_coverage/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_coverage/scenario_coverage/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_coverage/scenario_coverage/scenario_batch_execution.py b/scenario_coverage/scenario_coverage/scenario_batch_execution.py new file mode 100644 index 00000000..d687be80 --- /dev/null +++ b/scenario_coverage/scenario_coverage/scenario_batch_execution.py @@ -0,0 +1,148 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import sys +import argparse +import subprocess # nosec B404 +from threading import Thread +from collections import deque +from copy import deepcopy +import signal + + +class ScenarioBatchExecution(object): + + def __init__(self, args) -> None: + if not os.path.isdir(args.output_dir): + os.mkdir(args.output_dir) + self.output_dir = args.output_dir + + dir_content = os.listdir(args.scenario_dir) + self.scenarios = [] + for entry in dir_content: + if entry.endswith(".sce") or entry.endswith(".osc"): + self.scenarios.append(os.path.join(args.scenario_dir, entry)) + if not self.scenarios: + raise ValueError(f"Directory {args.scenario_dir} does not contain any scenarios.") + print(f"Detected {len(self.scenarios)} scenarios.") + self.launch_command = args.launch_command + if self.get_launch_command("", "") is None: + raise ValueError("Launch command does not contain {SCENARIO} and {JUNITXML}: " + " ".join(args.launch_command)) + print(f"Launch command: {self.launch_command}") + + def get_launch_command(self, scenario_name, junitxml): + launch_command = deepcopy(self.launch_command) + scenario_replaced = False + junitxml_replaced = False + for i in range(0, len(launch_command)): # pylint: disable=consider-using-enumerate + if "{SCENARIO}" in launch_command[i]: + launch_command[i] = launch_command[i].replace('{SCENARIO}', scenario_name) + scenario_replaced = True + if "{JUNITXML}" in launch_command[i]: + launch_command[i] = launch_command[i].replace('{JUNITXML}', junitxml) + junitxml_replaced = True + if scenario_replaced and junitxml_replaced: + return launch_command + else: + return None + + def run(self) -> bool: + + def log_output(out, buffer): + try: + for line in iter(out.readline, b''): + msg = line.decode().strip() + print(msg) + buffer.append(msg) + out.close() + except ValueError: + pass + + for scenario in self.scenarios: + output_file_path = os.path.join(self.output_dir, os.path.splitext(os.path.basename(scenario))[0]) + + launch_command = self.get_launch_command(scenario, output_file_path + '_result.xml') + output = deque() + log_cmd = " ".join(launch_command) + print(f"### For scenario {scenario}, executing process: '{log_cmd}'") + process = subprocess.Popen(launch_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + + log_stdout_thread = Thread(target=log_output, args=(process.stdout, output, )) + log_stdout_thread.daemon = True # die with the program + log_stdout_thread.start() + + log_stderr_thread = Thread(target=log_output, args=(process.stderr, output, )) + log_stderr_thread.daemon = True # die with the program + log_stderr_thread.start() + + print(f"### Waiting for process to finish...") + try: + process.wait() + except KeyboardInterrupt: + print("### Interrupted by user. Sending SIGINT...") + process.send_signal(signal.SIGINT) + try: + process.wait(timeout=20) + return False + except subprocess.TimeoutExpired: + print("### Process not stopped after 20s. Sending SIGKILL...") + process.send_signal(signal.SIGKILL) + try: + process.wait(timeout=10) + return False + except subprocess.TimeoutExpired: + print("### Process not stopped after 10s.") + return False + ret = process.returncode + + print(f"### Storing results in {self.output_dir}...") + + with open(output_file_path + '.log', 'w') as out: + for line in output: + out.write(line + '\n') + if ret: + print("### Process failed.") + else: + print("### Process finished successfully.") + + return True + + +def main(): + """ + main function + """ + parser = argparse.ArgumentParser() + parser.add_argument('-i', '--scenario-dir', type=str, help='Directory containing the scenarios') + parser.add_argument('-o', '--output-dir', type=str, help='Directory containing the output', default='out') + parser.add_argument('launch_command', nargs='+') + args = parser.parse_args(sys.argv[1:]) + + try: + scenario_batch_execution = ScenarioBatchExecution(args) + except Exception as e: # pylint: disable=broad-except + print(f"Error while initializing batch execution: {e}") + sys.exit(1) + if scenario_batch_execution.run(): + sys.exit(0) + else: + print("Error during batch executing!") + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/scenario_coverage/scenario_coverage/scenario_variation.py b/scenario_coverage/scenario_coverage/scenario_variation.py new file mode 100644 index 00000000..2bd56702 --- /dev/null +++ b/scenario_coverage/scenario_coverage/scenario_variation.py @@ -0,0 +1,153 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import sys +import argparse +from copy import deepcopy + +import yaml + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.model.model_resolver import resolve_internal_model +from scenario_execution_base.model.types import RelationExpression, ListExpression, print_tree, serialize +from scenario_execution_base.utils.logging import Logger + + +class ScenarioVariation(object): + + def __init__(self, target_dir, scenario, log_model, debug) -> None: + self.logger = Logger('scenario_variation') + self.target_dir = target_dir + self.scenario = scenario + self.log_model = log_model + self.debug = debug + + def get_variations(self, variant_element: RelationExpression): + base_element = deepcopy(variant_element) + base_element.operator = '==' + base_element.delete_child(base_element.get_child_with_expected_type(1, ListExpression)) + variations = [] + list_expression = variant_element.get_child_with_expected_type(1, ListExpression) + for child in list_expression.get_children(): + variation = deepcopy(base_element) + variation.set_children(child) + variations.append(variation) + return variations + + def run(self) -> bool: + model = self.load_model() + if model is None: + return False + + models = self.generate_concrete_models(model) + if not models: + return False + + return self.save_resulting_scenarios(models) + + def load_model(self): + parser = OpenScenario2Parser(self.logger) + parsed_tree, errors = parser.parse_file(self.scenario, self.log_model) + if errors: + return None + + return parser.load_internal_model(parsed_tree, self.scenario, self.log_model, self.debug) + + def get_next_variation_element(self, elem): + if isinstance(elem, RelationExpression): + if elem.operator == 'in': + return elem + else: + return None + else: + for child in elem.get_children(): + elem = self.get_next_variation_element(child) + if elem: + return elem + return None + + def generate_concrete_models(self, model): + models = [model] + while True: + # The following loop always looks at the first element in models. + # If it contains a variation_element the element is removed and the + # resulting models are appended at the back. + variation_element = self.get_next_variation_element(models[0]) + if variation_element is None: + print("No further variation") + return models + print(f"Creating models for variation model {variation_element}") + # remove model with variation from list + model = models[0] + models.remove(model) + + # remove original variation element + parent = variation_element.get_parent() + parent.delete_child(variation_element) + + # set resolved variations in copies of original model + variations = self.get_variations(variation_element) + for variation in variations: + parent.set_children(variation) + variation_model = deepcopy(model) + models.append(variation_model) + parent.delete_child(variation) + + def save_resulting_scenarios(self, models): + idx = 0 + file_path = os.path.join(self.target_dir, os.path.splitext(os.path.basename(self.scenario))[0]) + for model in models: + print("-----------------") + test_resolve = deepcopy(model) + serialize_data = serialize(model)['CompilationUnit']['_children'] + success = resolve_internal_model(test_resolve, self.logger, False) + if not success: + print(f"Error: model is not resolvable.") + return False + print_tree(model, self.logger) + filename = file_path + str(idx) + '.sce' + print(f"Storing model in {filename}") + with open(filename, 'w') as output: + yaml.safe_dump(serialize_data, output, sort_keys=False) + idx += 1 + return True + + +def main(): + """ + main function + """ + parser = argparse.ArgumentParser() + parser.add_argument('-d', '--debug', action='store_true', help='debugging output') + parser.add_argument('-o', '--log-model', action='store_true', help='Produce tree output of parsed model content') + parser.add_argument('-t', '--target-dir', type=str, help='Target directory for concrete scenarios', default='out') + parser.add_argument('scenario', type=str, help='abstract scenario file') + args = parser.parse_args(sys.argv[1:]) + + if not os.path.isdir(args.target_dir): + os.mkdir(args.target_dir) + + scenario_variation = ScenarioVariation(args.target_dir, args.scenario, args.log_model, args.debug) + if scenario_variation.run(): + sys.exit(0) + else: + print("Error!") + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/scenario_coverage/scenarios/test_fault_injection.osc b/scenario_coverage/scenarios/test_fault_injection.osc new file mode 100644 index 00000000..8eaa74c0 --- /dev/null +++ b/scenario_coverage/scenarios/test_fault_injection.osc @@ -0,0 +1,26 @@ +import osc.ros + +scenario nav2_simulation_fault_injection: + turtlebot4: differential_drive_robot with: + keep(it.model == 'topic:///robot_description') + do parallel: + test_drive: serial: + turtlebot4.init_nav2() with: + keep(it.initial_pose.position == position_3d(x: 0.0m, y: 0.0m)) + record_bag() with: + keep(it.destination_dir == '/transfer') + keep(it.topics == '/scenario_status /robot_pose_loc /robot_pose_gt /cmd_vel /amcl_pose /odom /tf /tf_static') # whitespace-separated list of topics + set_node_parameter() with: + keep(it.node_name == 'laserscan_modification') + keep(it.parameter_name == 'gaussian_noise_std_deviation') + keep(it.parameter_value in ['0.0', '0.1', '0.2', '0.5']) + set_node_parameter() with: + keep(it.node_name == 'laserscan_modification') + keep(it.parameter_name == 'random_drop_percentage') + keep(it.parameter_value in ['0.0', '0.1','0.5', '0.9']) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 3.0m, y: -3.0m))) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 0.0m, y: 0.0m))) + emit end + time_out: serial: + wait elapsed(240s) + emit fail diff --git a/scenario_coverage/scenarios/test_fault_injection_drop.osc b/scenario_coverage/scenarios/test_fault_injection_drop.osc new file mode 100644 index 00000000..f9e95530 --- /dev/null +++ b/scenario_coverage/scenarios/test_fault_injection_drop.osc @@ -0,0 +1,20 @@ +import osc.ros + +scenario nav2_simulation_fault_injection: + turtlebot4: differential_drive_robot with: + keep(it.model == 'topic:///robot_description') + do parallel: + test_drive: serial: + turtlebot4.init_nav2() with: + keep(it.initial_pose.position == position_3d(x: 0.0m, y: 0.0m)) + #keep(it.use_initial_pose == false) + set_node_parameter() with: + keep(it.node_name == 'laserscan_modification') + keep(it.parameter_name == 'random_drop_percentage') + keep(it.parameter_value in ['0.1','0.5', '0.9']) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 3.0m, y: -3.0m))) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 0.0m, y: 0.0m))) + emit end + time_out: serial: + wait elapsed(240s) + emit fail diff --git a/scenario_coverage/scenarios/test_fault_injection_noise.osc b/scenario_coverage/scenarios/test_fault_injection_noise.osc new file mode 100644 index 00000000..0a932cb0 --- /dev/null +++ b/scenario_coverage/scenarios/test_fault_injection_noise.osc @@ -0,0 +1,20 @@ +import osc.ros + +scenario nav2_simulation_fault_injection: + turtlebot4: differential_drive_robot with: + keep(it.model == 'topic:///robot_description') + do parallel: + test_drive: serial: + turtlebot4.init_nav2() with: + keep(it.initial_pose.position == position_3d(x: 0.0m, y: 0.0m)) + #keep(it.use_initial_pose == false) + set_node_parameter() with: + keep(it.node_name == 'laserscan_modification') + keep(it.parameter_name == 'gaussian_noise_std_deviation') + keep(it.parameter_value in ['0.1','0.2', '0.5']) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 3.0m, y: -3.0m))) + turtlebot4.nav_to_pose(pose_3d(position_3d(x: 0.0m, y: 0.0m))) + emit end + time_out: serial: + wait elapsed(240s) + emit fail diff --git a/scenario_coverage/scenarios/test_log.osc b/scenario_coverage/scenarios/test_log.osc new file mode 100644 index 00000000..b18d658f --- /dev/null +++ b/scenario_coverage/scenarios/test_log.osc @@ -0,0 +1,7 @@ +import osc.helpers + +scenario test_log: + do serial: + log() with: + keep(it.msg in ["foo", "bar"]) + emit end diff --git a/scenario_coverage/scenarios/test_nav_to_pose.osc b/scenario_coverage/scenarios/test_nav_to_pose.osc new file mode 100644 index 00000000..4fa749f3 --- /dev/null +++ b/scenario_coverage/scenarios/test_nav_to_pose.osc @@ -0,0 +1,19 @@ +import osc.ros + +scenario nav2_simulation_nav_to_pose: + turtlebot4: differential_drive_robot with: + keep(it.model == 'topic:///robot_description') + do parallel: + test_drive: serial: + turtlebot4.init_nav2() with: + keep(it.initial_pose.position == position_3d(x: 0.0m, y: 0.0m)) + #keep(it.use_initial_pose == false) + turtlebot4.nav_to_pose() with: + keep(it.goal_pose in [ + pose_3d(position: position_3d(x: 3.0m, y: -3.0m), orientation: orientation_3d(yaw: 0.0rad)), + pose_3d(position: position_3d(x: 3.0m, y: 3.0m), orientation: orientation_3d(yaw: 0.0rad)) + ]) + emit end + time_out: serial: + wait elapsed(240s) + emit fail diff --git a/scenario_coverage/setup.cfg b/scenario_coverage/setup.cfg new file mode 100644 index 00000000..d7c3c20d --- /dev/null +++ b/scenario_coverage/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/scenario_coverage +[install] +install_scripts=$base/lib/scenario_coverage diff --git a/scenario_coverage/setup.py b/scenario_coverage/setup.py new file mode 100644 index 00000000..39cda4b3 --- /dev/null +++ b/scenario_coverage/setup.py @@ -0,0 +1,45 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from glob import glob +import os +from setuptools import find_packages, setup + +PACKAGE_NAME = 'scenario_coverage' + +setup( + name=PACKAGE_NAME, + version='1.0.0', + packages=find_packages(), + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + PACKAGE_NAME]), + ('share/' + PACKAGE_NAME, ['package.xml']), + (os.path.join('share', PACKAGE_NAME, 'launch'), glob('launch/*launch.py')) + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Robotics Scenario Execution', + license='Apache License 2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'scenario_variation = scenario_coverage.scenario_variation:main', + ], + }, +) diff --git a/scenario_coverage/test/test_variations.py b/scenario_coverage/test/test_variations.py new file mode 100644 index 00000000..f2f2eea1 --- /dev/null +++ b/scenario_coverage/test/test_variations.py @@ -0,0 +1,171 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import unittest + +from scenario_coverage.scenario_variation import ScenarioVariation +from scenario_execution_base.model.model_file_loader import ModelFileLoader +from scenario_execution_base.model.model_resolver import resolve_internal_model +from scenario_execution_base.utils.logging import Logger +import tempfile +import copy +import os + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + def setUp(self) -> None: + self.tmpdir = tempfile.TemporaryDirectory() + + def run_coverage(self, scenario_content): + fp = tempfile.NamedTemporaryFile(suffix='.osc', mode='w', delete=False) + fp.write(scenario_content) + fp.close() + coverage = ScenarioVariation(self.tmpdir.name, fp.name, False, False) + model = coverage.load_model() + del fp + return model, coverage + + def test_struct_variation(self): + scenario_content = """ + +struct base_struct: + base1: string + base2: string = "predefined" + +struct test_struct: + member1: string + member2: base_struct + +action foo: + param1: test_struct + +scenario test: + do serial: + foo() with: + keep(it.param1 in [test_struct(member1: 'test1', member2: base_struct(base1: 'foo1')), test_struct(member1: 'test2', member2: base_struct(base1: 'foo2'))]) +""" + model, coverage = self.run_coverage(scenario_content) + self.assertIsNotNone(model) + models = coverage.generate_concrete_models(model) + self.assertIsNotNone(models) + self.assertEqual(2, len(models)) + + # Model 1 + model = copy.deepcopy(models[0]) + ret = resolve_internal_model(model, Logger('test'), False) + self.assertTrue(ret) + elem = model._ModelElement__children[3]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': {'member1': 'test1', 'member2': {'base1': 'foo1', 'base2': 'predefined'}}}) + + # Model 2 + model = copy.deepcopy(models[1]) + ret = resolve_internal_model(model, Logger('test'), False) + self.assertTrue(ret) + elem = model._ModelElement__children[3]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': {'member1': 'test2', 'member2': {'base1': 'foo2', 'base2': 'predefined'}}}) + + result = coverage.save_resulting_scenarios(models) + self.assertTrue(result) + + dir_content = os.listdir(self.tmpdir.name) + scenarios = [] + for entry in dir_content: + if entry.endswith(".sce"): + scenarios.append(os.path.join(self.tmpdir.name, entry)) + self.assertEqual(2, len(scenarios)) + + # read from file + scenario0 = None + for scenario in scenarios: + if scenario.endswith("0.sce"): + scenario0 = scenario + parser = ModelFileLoader(Logger('test')) + model = parser.load_file(scenario0, True) + self.assertIsNotNone(model) + success = resolve_internal_model(model, parser.logger, False) + self.assertTrue(success) + elem = model._ModelElement__children[3]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': {'member1': 'test1', 'member2': {'base1': 'foo1', 'base2': 'predefined'}}}) + + def test_multi_variation(self): + scenario_content = """ +action foo: + param1: string + param2: string + +scenario test: + do serial: + foo() with: + keep(it.param1 in ['A1', 'A2']) + keep(it.param2 in ['B1', 'B2', 'B3']) +""" + model, coverage = self.run_coverage(scenario_content) + self.assertIsNotNone(model) + models = coverage.generate_concrete_models(model) + self.assertIsNotNone(models) + self.assertEqual(6, len(models)) + + # Model 1 + model = copy.deepcopy(models[0]) + ret = resolve_internal_model(model, Logger('test'), False) + self.assertTrue(ret) + elem = model._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': 'A1', 'param2': 'B1'}) + + # Model 2 + model = copy.deepcopy(models[1]) + ret = resolve_internal_model(model, Logger('test'), False) + self.assertTrue(ret) + elem = model._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': 'A1', 'param2': 'B2'}) + + # Model 3 + model = copy.deepcopy(models[2]) + ret = resolve_internal_model(model, Logger('test'), False) + self.assertTrue(ret) + elem = model._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': 'A1', 'param2': 'B3'}) + + # Model 4 + model = copy.deepcopy(models[3]) + ret = resolve_internal_model(model, Logger('test'), False) + self.assertTrue(ret) + elem = model._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': 'A2', 'param2': 'B1'}) + + # Model 5 + model = copy.deepcopy(models[4]) + ret = resolve_internal_model(model, Logger('test'), False) + self.assertTrue(ret) + elem = model._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': 'A2', 'param2': 'B2'}) + + # Model 6 + model = copy.deepcopy(models[5]) + ret = resolve_internal_model(model, Logger('test'), False) + self.assertTrue(ret) + elem = model._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + value = elem.get_resolved_value() + self.assertEqual(value, {'param1': 'A2', 'param2': 'B3'}) diff --git a/scenario_execution/MANIFEST.in b/scenario_execution/MANIFEST.in new file mode 100644 index 00000000..a584b473 --- /dev/null +++ b/scenario_execution/MANIFEST.in @@ -0,0 +1 @@ +include scenario_execution/lib_osc/*.osc \ No newline at end of file diff --git a/scenario_execution/README.md b/scenario_execution/README.md new file mode 100644 index 00000000..a40f319a --- /dev/null +++ b/scenario_execution/README.md @@ -0,0 +1,7 @@ +# Scenario Execution + +The `scenario_execution` package is the ROS2 middleware implementation of the scenario execution. It uses the `py_trees_ros` packages as the `py_trees`'s implementation for ROS2. + +It provides the following scenario execution libraries: + +- `ros.osc`: ROS specific actions like topic publish and service call. diff --git a/scenario_execution/launch/scenario_launch.py b/scenario_execution/launch/scenario_launch.py new file mode 100644 index 00000000..75aef44c --- /dev/null +++ b/scenario_execution/launch/scenario_launch.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Scenario Launch """ +from launch_ros.actions import Node +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, Shutdown, LogInfo +from launch.substitutions import LaunchConfiguration +from launch.conditions import IfCondition, UnlessCondition + + +def generate_launch_description(): + """ generate launch description """ + scenario = LaunchConfiguration('scenario') + debug = LaunchConfiguration('debug') + live_tree = LaunchConfiguration('live_tree') + log_model = LaunchConfiguration('log_model') + log_level = LaunchConfiguration('log_level') + scenario_execution = LaunchConfiguration('scenario_execution') + scenario_status = LaunchConfiguration('scenario_status') + test_output = LaunchConfiguration('test_output') + + return LaunchDescription([ + DeclareLaunchArgument('scenario', description='Scenario file to execute'), + DeclareLaunchArgument('debug', description='Debug output', default_value='False'), + DeclareLaunchArgument('live_tree', default_value='False', + description='output live state of scenario'), + DeclareLaunchArgument('log_model', default_value='False', + description='log parsed model'), + DeclareLaunchArgument('scenario_execution', default_value='True', + description='Wether to execute scenario execution'), + DeclareLaunchArgument('scenario_status', default_value='True', + description='Wether to execute scenario status'), + DeclareLaunchArgument('log_level', default_value='info', + description='Log level for scenario execution'), + DeclareLaunchArgument('test_output', description='Test output file', default_value=''), + + Node( + package='scenario_execution', + executable='scenario_execution', + name='scenario_execution', + output='screen', + additional_env={'PYTHONUNBUFFERED': '1'}, + arguments=['--ros-args', '--log-level', log_level], + parameters=[{ + 'use_sim_time': False, + 'debug': debug, + 'live_tree': live_tree, + 'log_model': log_model, + 'test_output': test_output, + 'scenario': scenario + }], + on_exit=Shutdown()), + + Node( + condition=IfCondition(scenario_status), + package='scenario_status', + executable='scenario_status_node', + name='scenario_status_node', + parameters=[{ + 'bt_snapshot_topic': '/bt_snapshot', + 'scenario_status_topic': '/scenario_status', + 'snapshot_srv_name': '/scenario_execution/snapshot_streams/open', + + }], + output='screen' + ), + + # Parse log for message on how to execute scenario separately + LogInfo( + condition=UnlessCondition(scenario_execution), + msg=["Skipping: ros2 launch scenario_execution scenario_launch.py scenario:=", + scenario, ' log_level:="', log_level, '"']), + ]) diff --git a/scenario_execution/package.xml b/scenario_execution/package.xml new file mode 100644 index 00000000..2941d7c3 --- /dev/null +++ b/scenario_execution/package.xml @@ -0,0 +1,30 @@ + + + + scenario_execution + 1.0.0 + Scenario Execution for ROS + Intel Labs + Intel Labs + Apache-2.0 + + scenario_execution_base + + rclpy + py_trees + py_trees_ros + visualization_msgs + nav2_simple_commander + xacro + rcl_interfaces + scenario_status + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/scenario_execution/resource/scenario_execution b/scenario_execution/resource/scenario_execution new file mode 100644 index 00000000..e69de29b diff --git a/scenario_execution/scenario_execution/__init__.py b/scenario_execution/scenario_execution/__init__.py new file mode 100644 index 00000000..c48c9ebf --- /dev/null +++ b/scenario_execution/scenario_execution/__init__.py @@ -0,0 +1,27 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Main entry for scenario execution """ + +from . import actions +from .logging_ros import RosLogger +from .scenario_execution_ros import ROSScenarioExecution + +__all__ = [ + 'actions', + 'RosLogger', + 'ROSScenarioExecution' +] diff --git a/scenario_execution/scenario_execution/actions/__init__.py b/scenario_execution/scenario_execution/actions/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_execution/scenario_execution/actions/conversions.py b/scenario_execution/scenario_execution/actions/conversions.py new file mode 100644 index 00000000..2fa7b521 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/conversions.py @@ -0,0 +1,73 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" common conversions """ + +import operator +from rclpy.qos import QoSPresetProfiles +import py_trees + + +def get_qos_preset_profile(qos_profile): + """ + Get qos preset for enum value + """ + if qos_profile[0] == 'parameters': + return QoSPresetProfiles.PARAMETERS.value + elif qos_profile[0] == 'parameter_events': + return QoSPresetProfiles.PARAMETER_EVENTS.value + elif qos_profile[0] == 'sensor_data': + return QoSPresetProfiles.SENSOR_DATA.value + elif qos_profile[0] == 'service_default': + return QoSPresetProfiles.SERVICE_DEFAULT.value # pylint: disable=no-member + elif qos_profile[0] == 'system_default': + return QoSPresetProfiles.SYSTEM_DEFAULT.value + else: + raise ValueError(f"Invalid qos_profile: {qos_profile}") + + +def get_comparison_operator(operator_val): # pylint: disable=too-many-return-statements + """ + Get comparison operator for enum value + """ + if operator_val[0] == 'lt': + return operator.lt + elif operator_val[0] == 'le': + return operator.le + elif operator_val[0] == 'eq': + return operator.eq + elif operator_val[0] == 'ne': + return operator.ne + elif operator_val[0] == 'ge': + return operator.ge + elif operator_val[0] == 'gt': + return operator.gt + else: + raise ValueError(f"Invalid comparison_operator: {operator_val}") + + +def get_clearing_policy(clearing_policy): + """ + Get clearing policy for enum value + """ + if clearing_policy[0] == 'on_initialise': + return py_trees.common.ClearingPolicy.ON_INITIALISE + elif clearing_policy[0] == 'on_success': + return py_trees.common.ClearingPolicy.ON_SUCCESS + elif clearing_policy[0] == 'never': + return py_trees.common.ClearingPolicy.NEVER + else: + raise ValueError(f"Invalid clearing_policy: {clearing_policy}") diff --git a/scenario_execution/scenario_execution/actions/init_nav2.py b/scenario_execution/scenario_execution/actions/init_nav2.py new file mode 100644 index 00000000..c60ce312 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/init_nav2.py @@ -0,0 +1,238 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum + + +from geometry_msgs.msg import PoseWithCovarianceStamped +from lifecycle_msgs.srv import GetState +from rclpy.node import Node +from rclpy.qos import QoSDurabilityPolicy, QoSHistoryPolicy +from rclpy.qos import QoSProfile, QoSReliabilityPolicy +from rclpy.callback_groups import ReentrantCallbackGroup +from rclpy.time import Time +from rclpy.duration import Duration +from tf2_ros import Buffer +from datetime import datetime, timedelta +import py_trees + +from .nav2_common import NamespaceAwareBasicNavigator, get_pose_stamped, NamespacedTransformListener + + +class InitNav2State(Enum): + """ + States for executing a initialization of nav2 + """ + IDLE = 1 + LOCALIZER_STATE_REQUESTED = 2 + LOCALIZER_STATE_RECEIVED = 3 + LOCALIZER_STATE_ACTIVE = 4 + WAIT_FOR_INITIAL_POSE = 5 + WAIT_FOR_MAP_BASELINK_TF = 6 + MAP_BASELINK_TF_RECEIVED = 7 + NAVIGATOR_STATE_REQUESTED = 8 + NAVIGATOR_STATE_RECEIVED = 9 + NAVIGATOR_ACTIVE = 10 + DONE = 11 + FAILURE = 12 + + +class InitNav2(py_trees.behaviour.Behaviour): + """ + Class for initializing nav2 by setting an initial pose and activate required nodes + + """ + + def __init__(self, name, associated_actor, initial_pose: list, base_frame_id: str, wait_for_initial_pose: bool, use_initial_pose: bool, namespace_override: str): + super().__init__('InitNav2Action') + self.initial_pose = initial_pose + self.base_frame_id = base_frame_id + self.wait_for_initial_pose = wait_for_initial_pose + self.use_initial_pose = use_initial_pose + self.namespace = associated_actor["namespace"] + self.node = None + self.future = None + self.current_state = InitNav2State.IDLE + self.nav = None + self.bt_navigator_state_client = None + self.amcl_state_client = None + self.localization_pose_sub = None + self.retry_count = None + self.service_called_timestamp = None + self.localizer_state = None + self.navigator_state = None + self.tf_buffer = None + self.tf_listener = None + if namespace_override: + self.namespace = namespace_override + + def setup(self, **kwargs): + """ + Setup ROS2 node and service client + + """ + try: + self.node: Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + self.tf_buffer = Buffer() + self.tf_listener = NamespacedTransformListener( + node=self.node, buffer=self.tf_buffer, tf_topic=self.namespace + "/tf", tf_static_topic=self.namespace + "/tf_static") + + self.nav = NamespaceAwareBasicNavigator( + node_name="basic_nav_init_nav2", namespace=self.namespace) + self.bt_navigator_state_client = self.node.create_client( + GetState, self.namespace + '/bt_navigator/get_state', + callback_group=ReentrantCallbackGroup()) + self.amcl_state_client = self.node.create_client( + GetState, self.namespace + '/amcl/get_state', + callback_group=ReentrantCallbackGroup()) + + amcl_pose_qos = QoSProfile( + durability=QoSDurabilityPolicy.TRANSIENT_LOCAL, + reliability=QoSReliabilityPolicy.RELIABLE, + history=QoSHistoryPolicy.KEEP_LAST, + depth=1) + + self.localization_pose_sub = self.node.create_subscription(PoseWithCovarianceStamped, + self.namespace + '/amcl_pose', + self._amcl_pose_callback, + amcl_pose_qos, + callback_group=ReentrantCallbackGroup()) + + def update(self) -> py_trees.common.Status: + """ + Execute states + """ + self.logger.debug(f"Current State {self.current_state}") + result = py_trees.common.Status.FAILURE + if self.current_state == InitNav2State.IDLE: + if self.use_initial_pose: + self.current_state = InitNav2State.LOCALIZER_STATE_REQUESTED + if self.retry_count is None: + self.retry_count = 1000 + else: + self.retry_count -= 1 + if self.future: + self.amcl_state_client.remove_pending_request(self.future) + self.future.cancel() + + if self.retry_count > 0: + req = GetState.Request() + self.feedback_message = f"Waiting for localizer to become active. Try {1001 - self.retry_count}" # pylint: disable= attribute-defined-outside-init + self.future = self.amcl_state_client.call_async(req) + self.service_called_timestamp = datetime.now() + self.future.add_done_callback(self._get_state_done_callback) + result = py_trees.common.Status.RUNNING + else: + self.current_state = InitNav2State.WAIT_FOR_MAP_BASELINK_TF + self.feedback_message = f"Not using initial pose (probably because we localize with slam), waiting for map --> base transform" # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.LOCALIZER_STATE_REQUESTED: + timeout = timedelta(seconds=1) + if timeout < datetime.now() - self.service_called_timestamp: + self.feedback_message = f"Localizer state request timed out after 1s. Requesting again..." # pylint: disable= attribute-defined-outside-init + self.current_state = InitNav2State.IDLE + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.LOCALIZER_STATE_RECEIVED: + if self.localizer_state == "active": + self.current_state = InitNav2State.LOCALIZER_STATE_ACTIVE + self.retry_count = None + else: + self.feedback_message = f"Localizer expected to be active, but is '{self.localizer_state}'. Retrying..." # pylint: disable= attribute-defined-outside-init + self.current_state = InitNav2State.IDLE + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.LOCALIZER_STATE_ACTIVE: + self.current_state = InitNav2State.WAIT_FOR_INITIAL_POSE + if self.wait_for_initial_pose: + self.feedback_message = f"Waiting for externally set initial pose." # pylint: disable= attribute-defined-outside-init + else: + initial_pose = get_pose_stamped( + self.nav.get_clock().now().to_msg(), self.initial_pose) + self.feedback_message = f"Set initial pose." # pylint: disable= attribute-defined-outside-init + self.nav.setInitialPose(initial_pose) + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.WAIT_FOR_INITIAL_POSE: + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.WAIT_FOR_MAP_BASELINK_TF: + if self.tf_buffer.can_transform("map", self.base_frame_id, Time(seconds=0), Duration(seconds=0)): + self.feedback_message = f"Transform map -> {self.base_frame_id} got available." # pylint: disable= attribute-defined-outside-init + self.current_state = InitNav2State.MAP_BASELINK_TF_RECEIVED + else: + self.feedback_message = f"Waiting for transform map -> {self.base_frame_id} to get available..." # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.MAP_BASELINK_TF_RECEIVED: + self.current_state = InitNav2State.NAVIGATOR_STATE_REQUESTED + if self.retry_count is None: + self.retry_count = 1000 + else: + self.retry_count -= 1 + if self.future: + self.bt_navigator_state_client.remove_pending_request(self.future) + self.future.cancel() + if self.retry_count > 0: + req = GetState.Request() + self.feedback_message = f"Request navigator state. Try {1001 - self.retry_count}" # pylint: disable= attribute-defined-outside-init + self.future = self.bt_navigator_state_client.call_async(req) + self.service_called_timestamp = datetime.now() + self.future.add_done_callback(self._get_state_done_callback) + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.NAVIGATOR_STATE_REQUESTED: + timeout = timedelta(seconds=1) + if timeout < datetime.now() - self.service_called_timestamp: + self.feedback_message = f"Navigator state request timed out after 1s. Requesting again..." # pylint: disable= attribute-defined-outside-init + self.current_state = InitNav2State.MAP_BASELINK_TF_RECEIVED + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.NAVIGATOR_STATE_RECEIVED: + if self.navigator_state == "active": + self.feedback_message = f"Navigator active." # pylint: disable= attribute-defined-outside-init + self.current_state = InitNav2State.NAVIGATOR_ACTIVE + self.retry_count = None + else: + self.feedback_message = f"Navigator expected to be active, but is {self.navigator_state}. Retrying..." # pylint: disable= attribute-defined-outside-init + self.current_state = InitNav2State.MAP_BASELINK_TF_RECEIVED + result = py_trees.common.Status.RUNNING + elif self.current_state == InitNav2State.NAVIGATOR_ACTIVE: + result = py_trees.common.Status.SUCCESS + self.current_state = InitNav2State.DONE + elif self.current_state == InitNav2State.DONE: + self.logger.debug("Nothing to do!") + else: + self.logger.error(f"Invalid state {self.current_state}") + + return result + + def _get_state_done_callback(self, future): + """ + Callback function when the GetState future is done + """ + self.logger.debug(f"Received state {future.result()}") + if self.current_state == InitNav2State.LOCALIZER_STATE_REQUESTED: + self.localizer_state = future.result().current_state.label + self.current_state = InitNav2State.LOCALIZER_STATE_RECEIVED + elif self.current_state == InitNav2State.NAVIGATOR_STATE_REQUESTED: + self.navigator_state = future.result().current_state.label + self.current_state = InitNav2State.NAVIGATOR_STATE_RECEIVED + + def _amcl_pose_callback(self, msg): + """ + Callback function when amcl pose is received + """ + if self.current_state == InitNav2State.WAIT_FOR_INITIAL_POSE: + self.current_state = InitNav2State.WAIT_FOR_MAP_BASELINK_TF diff --git a/scenario_execution/scenario_execution/actions/nav2_common.py b/scenario_execution/scenario_execution/actions/nav2_common.py new file mode 100644 index 00000000..d10a50f8 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/nav2_common.py @@ -0,0 +1,208 @@ +# Copyright 2021 Samsung Research America +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from geometry_msgs.msg import PoseStamped, PoseWithCovarianceStamped +from rclpy.qos import QoSDurabilityPolicy, QoSHistoryPolicy +from rclpy.qos import QoSProfile, QoSReliabilityPolicy +from rclpy.qos import DurabilityPolicy +from rclpy.qos import HistoryPolicy +from rclpy.node import Node +from rclpy.action import ActionClient +from rclpy.callback_groups import ReentrantCallbackGroup +from rclpy.executors import SingleThreadedExecutor + +from transforms3d.taitbryan import euler2quat + +from nav2_msgs.action import BackUp, Spin +from nav2_msgs.action import ComputePathThroughPoses, ComputePathToPose +from nav2_msgs.action import FollowPath, FollowWaypoints, NavigateThroughPoses, NavigateToPose +from nav2_msgs.srv import ClearEntireCostmap, GetCostmap, LoadMap + +from nav2_simple_commander.robot_navigator import BasicNavigator # pylint: disable=import-error +from tf2_ros import Buffer +from tf2_msgs.msg import TFMessage +from threading import Thread + +from typing import Optional +from typing import Union + + +def get_pose_stamped(timestamp, in_pose): + """ + Convert pose list to PoseStamped + """ + pose = PoseStamped() + pose.header.frame_id = 'map' + pose.header.stamp = timestamp + pose.pose.position.x = in_pose['position']['x'] + pose.pose.position.y = in_pose['position']['y'] + pose.pose.position.z = in_pose['position']['z'] + + # euler2quat() requires "zyx" convention, + # while in YAML, we define as pitch-roll-yaw (xyz), since it's more intuitive. + quaternion = euler2quat(in_pose['orientation']['yaw'], + in_pose['orientation']['roll'], + in_pose['orientation']['pitch']) + pose.pose.orientation.w = quaternion[0] + pose.pose.orientation.x = quaternion[1] + pose.pose.orientation.y = quaternion[2] + pose.pose.orientation.z = quaternion[3] + return pose + + +class NamespacedTransformListener: + """ + :class:`TransformListener` is a convenient way to listen for coordinate frame transformation info. + This class takes an object that instantiates the :class:`BufferInterface` interface, to which + it propagates changes to the tf frame graph. + """ + + def __init__( + self, + buffer: Buffer, + node: Node, + *, + tf_topic: str = '/tf', + tf_static_topic: str = '/tf_static', + spin_thread: bool = False, + qos: Optional[Union[QoSProfile, int]] = None, + static_qos: Optional[Union[QoSProfile, int]] = None + ) -> None: + """ + Constructor. + + :param buffer: The buffer to propagate changes to when tf info updates. + :param node: The ROS2 node. + :param spin_thread: Whether to create a dedidcated thread to spin this node. + :param qos: A QoSProfile or a history depth to apply to subscribers. + :param static_qos: A QoSProfile or a history depth to apply to tf_static subscribers. + """ + if qos is None: + qos = QoSProfile( + depth=100, + durability=DurabilityPolicy.VOLATILE, + history=HistoryPolicy.KEEP_LAST, + ) + if static_qos is None: + static_qos = QoSProfile( + depth=100, + durability=DurabilityPolicy.TRANSIENT_LOCAL, + history=HistoryPolicy.KEEP_LAST, + ) + self.buffer = buffer + self.node = node + # Default callback group is mutually exclusive, which would prevent waiting for transforms + # from another callback in the same group. + self.group = ReentrantCallbackGroup() + self.tf_sub = node.create_subscription( + TFMessage, tf_topic, self.callback, qos, callback_group=self.group) + self.tf_static_sub = node.create_subscription( + TFMessage, tf_static_topic, self.static_callback, static_qos, callback_group=self.group) + + if spin_thread: + self.executor = SingleThreadedExecutor() + + def run_func(): + self.executor.add_node(self.node) + self.executor.spin() + self.executor.remove_node(self.node) + + self.dedicated_listener_thread = Thread(target=run_func) + self.dedicated_listener_thread.start() + + def __del__(self) -> None: + if hasattr(self, 'dedicated_listener_thread') and hasattr(self, 'executor'): + self.executor.shutdown() + self.dedicated_listener_thread.join() + + self.unregister() + + def unregister(self) -> None: + """ + Unregisters all tf subscribers. + """ + self.node.destroy_subscription(self.tf_sub) + self.node.destroy_subscription(self.tf_static_sub) + + def callback(self, data: TFMessage) -> None: + who = 'default_authority' + for transform in data.transforms: + self.buffer.set_transform(transform, who) + + def static_callback(self, data: TFMessage) -> None: + who = 'default_authority' + for transform in data.transforms: + self.buffer.set_transform_static(transform, who) + + +class NamespaceAwareBasicNavigator(BasicNavigator): + """ + Subclass of BasicNavigator to support namespaces + """ + + def __init__(self, node_name, namespace): + """ + Clone of BasicNavigator init + support for namespaces + """ + super(BasicNavigator, self).__init__(node_name=node_name, # pylint: disable=bad-super-call + namespace=namespace) # pylint: disable=non-parent-init-called + + self.initial_pose = PoseStamped() + self.initial_pose.header.frame_id = 'map' + self.goal_handle = None + self.result_future = None + self.feedback = None + self.status = None + self.cb_group = ReentrantCallbackGroup() + + amcl_pose_qos = QoSProfile( + durability=QoSDurabilityPolicy.TRANSIENT_LOCAL, + reliability=QoSReliabilityPolicy.RELIABLE, + history=QoSHistoryPolicy.KEEP_LAST, + depth=1) + + self.initial_pose_received = False + self.nav_through_poses_client = ActionClient(self, + NavigateThroughPoses, + 'navigate_through_poses') + self.nav_to_pose_client = ActionClient(self, NavigateToPose, 'navigate_to_pose') + self.follow_waypoints_client = ActionClient(self, FollowWaypoints, 'follow_waypoints') + self.follow_path_client = ActionClient(self, FollowPath, 'follow_path') + self.compute_path_to_pose_client = ActionClient(self, ComputePathToPose, + 'compute_path_to_pose') + self.compute_path_through_poses_client = ActionClient(self, ComputePathThroughPoses, + 'compute_path_through_poses') + self.spin_client = ActionClient(self, Spin, 'spin') + self.backup_client = ActionClient(self, BackUp, 'backup') + self.localization_pose_sub = self.create_subscription(PoseWithCovarianceStamped, + 'amcl_pose', + self._amclPoseCallback, + amcl_pose_qos) + self.initial_pose_pub = self.create_publisher(PoseWithCovarianceStamped, + 'initialpose', + 10) + self.change_maps_srv = self.create_client(LoadMap, 'map_server/load_map') + self.clear_costmap_global_srv = self.create_client( + ClearEntireCostmap, 'global_costmap/clear_entirely_global_costmap', + callback_group=self.cb_group) + self.clear_costmap_local_srv = self.create_client( + ClearEntireCostmap, 'local_costmap/clear_entirely_local_costmap', + callback_group=self.cb_group) + self.get_costmap_global_srv = self.create_client( + GetCostmap, 'global_costmap/get_costmap', callback_group=self.cb_group) + self.get_costmap_local_srv = self.create_client( + GetCostmap, 'local_costmap/get_costmap', callback_group=self.cb_group) diff --git a/scenario_execution/scenario_execution/actions/nav_through_poses.py b/scenario_execution/scenario_execution/actions/nav_through_poses.py new file mode 100644 index 00000000..6efe26f6 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/nav_through_poses.py @@ -0,0 +1,125 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum + +from rclpy.node import Node +from rclpy.duration import Duration + +import py_trees + +from nav2_simple_commander.robot_navigator import TaskResult # pylint: disable=import-error + +from .nav2_common import NamespaceAwareBasicNavigator +from .nav2_common import get_pose_stamped + + +class NavThroughPosesState(Enum): + """ + States for executing a nav-through-poses with nav2 + """ + IDLE = 1 + NAV_TO_GOAL = 2 + DONE = 3 + + +class NavThroughPoses(py_trees.behaviour.Behaviour): + """ + Class to navigate through poses + """ + + def __init__(self, name: str, associated_actor, goal_poses: list, monitor_progress: bool, namespace_override: str): + super().__init__(name) + self.namespace = associated_actor["namespace"] + self.monitor_progress = monitor_progress + self.node = None + self.future = None + self.current_state = NavThroughPosesState.IDLE + self.nav = None + if namespace_override: + self.namespace = namespace_override + + if not isinstance(goal_poses, list): + raise TypeError(f'goal_poses needs to be list of position_3d, got {type(goal_poses)}.') + else: + self.goal_poses = goal_poses + + def setup(self, **kwargs): + """ + Setup ROS2 node and service client + + """ + try: + self.node: Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + self.nav = NamespaceAwareBasicNavigator( + node_name="basic_nav_nav_through_poses", namespace=self.namespace) + + def update(self) -> py_trees.common.Status: + """ + Execute states + """ + self.logger.debug(f"Current State {self.current_state}") + result = py_trees.common.Status.FAILURE + if self.current_state == NavThroughPosesState.IDLE: + self.current_state = NavThroughPosesState.NAV_TO_GOAL + goal_poses = [] + for pose in self.goal_poses: + goal_poses.append(get_pose_stamped(self.nav.get_clock().now().to_msg(), pose)) + self.feedback_message = "Execute navigation." # pylint: disable= attribute-defined-outside-init + go_to_pose_result = self.nav.goThroughPoses(goal_poses) + if go_to_pose_result: + if self.monitor_progress: + result = py_trees.common.Status.RUNNING + self.current_state = NavThroughPosesState.NAV_TO_GOAL + else: + result = py_trees.common.Status.SUCCESS + self.current_state = NavThroughPosesState.DONE + else: + self.current_state = NavThroughPosesState.DONE + result = py_trees.common.Status.FAILURE + elif self.current_state == NavThroughPosesState.NAV_TO_GOAL: + if not self.nav.isTaskComplete(): + feedback = self.nav.getFeedback() + if feedback: + self.feedback_message = 'Estimated time of arrival: ' + '{0:.0f}'.format( # pylint: disable= attribute-defined-outside-init + Duration.from_msg(feedback.estimated_time_remaining).nanoseconds / 1e9) + ' seconds.' + + if Duration.from_msg(feedback.navigation_time) > Duration(seconds=600): + self.nav.cancelTask() + result = py_trees.common.Status.RUNNING + else: + self.current_state = NavThroughPosesState.DONE + result = self.nav.getResult() + if result == TaskResult.SUCCEEDED: + self.feedback_message = 'Goal succeeded!' # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.SUCCESS + elif result == TaskResult.CANCELED: + self.feedback_message = 'Goal was canceled!' # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.FAILURE + elif result == TaskResult.FAILED: + self.feedback_message = 'Goal failed!' # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.FAILURE + elif self.current_state == NavThroughPosesState.DONE: + self.logger.debug("Nothing to do!") + else: + self.logger.error(f"Invalid state {self.current_state}") + + return result diff --git a/scenario_execution/scenario_execution/actions/nav_to_pose.py b/scenario_execution/scenario_execution/actions/nav_to_pose.py new file mode 100644 index 00000000..63379644 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/nav_to_pose.py @@ -0,0 +1,180 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from enum import Enum + +from rclpy.node import Node +from rclpy.duration import Duration +from rclpy.action import ActionClient + +import py_trees + +from .nav2_common import get_pose_stamped + +from nav2_msgs.action import NavigateToPose +from action_msgs.msg import GoalStatus + + +class NavToPoseState(Enum): + """ + States for executing a nav-to-pose with nav2 + """ + IDLE = 1 + NAV_TO_GOAL_REQUESTED = 2 + NAV_TO_GOAL = 3 + DONE = 4 + FAILURE = 5 + + +class NavToPose(py_trees.behaviour.Behaviour): + """ + Class to navigate to a pose + """ + + def __init__(self, name: str, associated_actor, goal_pose: list, monitor_progress: bool, action_topic: str, namespace_override: str) -> None: + super().__init__(name) + self.goal_pose = goal_pose + self.namespace = associated_actor["namespace"] + if namespace_override: + self.namespace = namespace_override + + self.monitor_progress = monitor_progress + self.action_topic = action_topic + self.node = None + self.future = None + self.current_state = NavToPoseState.IDLE + self.nav_to_pose_client = None + self.result_future = None + self.goal_handle = None + self.feedback = None + self.request_result = None + self.status = None + + def setup(self, **kwargs): + """ + Setup ROS2 node and service client + + """ + try: + self.node: Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + self.nav_to_pose_client = ActionClient( + self.node, NavigateToPose, self.namespace + '/' + self.action_topic) + + self.logger.debug("Waiting for 'NavigateToPose' action server") + wait_for_action_server_time = 30 + while wait_for_action_server_time > 0 and not self.nav_to_pose_client.wait_for_server(timeout_sec=1.0): + self.logger.info(f"'NavigateToPose' action server not available, waiting {wait_for_action_server_time}s...") + wait_for_action_server_time -= 1 + if wait_for_action_server_time == 0: + raise ValueError("Timeout while waiting for action server.") + + def update(self) -> py_trees.common.Status: + """ + Execute states + """ + self.logger.debug(f"Current State {self.current_state}") + result = py_trees.common.Status.FAILURE + if self.current_state == NavToPoseState.IDLE: + goal_pose = get_pose_stamped(self.node.get_clock().now().to_msg(), self.goal_pose) + self.feedback_message = "Executing navigation." # pylint: disable= attribute-defined-outside-init + + result = py_trees.common.Status.RUNNING + self.current_state = NavToPoseState.NAV_TO_GOAL_REQUESTED + self.navigate_to_pose(goal_pose) + elif self.current_state == NavToPoseState.NAV_TO_GOAL_REQUESTED: + if self.goal_handle: + if not self.goal_handle.accepted: + self.current_state = NavToPoseState.FAILURE + self.feedback_message = 'Goal was rejected!' # pylint: disable= attribute-defined-outside-init + else: + if self.monitor_progress: + result = py_trees.common.Status.RUNNING + self.current_state = NavToPoseState.NAV_TO_GOAL + + self.result_future = self.goal_handle.get_result_async() + else: + result = py_trees.common.Status.SUCCESS + self.current_state = NavToPoseState.DONE + else: + result = py_trees.common.Status.RUNNING + elif self.current_state == NavToPoseState.NAV_TO_GOAL: + if not self.is_task_complete(): + if self.feedback: + self.feedback_message = 'Estimated time of arrival: ' + '{0:.0f}'.format( # pylint: disable= attribute-defined-outside-init + Duration.from_msg(self.feedback.estimated_time_remaining).nanoseconds / 1e9) + ' seconds.' + + if Duration.from_msg(self.feedback.navigation_time) > Duration(seconds=600): + self.cancel_task() + result = py_trees.common.Status.RUNNING + else: + self.current_state = NavToPoseState.DONE + result = self.status + if result == GoalStatus.STATUS_SUCCEEDED: + self.feedback_message = 'Goal succeeded!' # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.SUCCESS + elif result == GoalStatus.STATUS_CANCELED: + self.feedback_message = 'Goal was canceled!' # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.FAILURE + elif result == GoalStatus.STATUS_ABORTED: + self.feedback_message = 'Goal failed!' # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.FAILURE + elif self.current_state == NavToPoseState.DONE: + self.logger.debug("Nothing to do!") + else: + self.logger.error(f"Invalid state {self.current_state}") + + return result + + def navigate_to_pose(self, pose, behavior_tree=''): + goal_msg = NavigateToPose.Goal() + goal_msg.pose = pose + goal_msg.behavior_tree = behavior_tree + + self.logger.info( + f'Navigating to goal: {pose.pose.position.x:.2f} {pose.pose.position.y:.2f}...') + future = self.nav_to_pose_client.send_goal_async(goal_msg, self.feedback_callback) + future.add_done_callback(self.send_goal_done_callback) + + def send_goal_done_callback(self, future): + self.goal_handle = future.result() + + def is_task_complete(self): + if not self.result_future: + return True + if self.result_future.result(): + self.status = self.result_future.result().status + if self.status != GoalStatus.STATUS_SUCCEEDED: + self.feedback_message = f'Task failed with status code: {self.status}' # pylint: disable= attribute-defined-outside-init + return True + else: + # Timed out, still processing, not complete yet + return False + + self.feedback_message = "Navigation executed." # pylint: disable= attribute-defined-outside-init + return True + + def cancel_task(self): + self.logger.info('Canceling current task.') + if self.result_future: + self.goal_handle.cancel_goal_async() + + def feedback_callback(self, msg): + self.feedback = msg.feedback diff --git a/scenario_execution/scenario_execution/actions/odometry_distance_traveled.py b/scenario_execution/scenario_execution/actions/odometry_distance_traveled.py new file mode 100644 index 00000000..65c3bac2 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/odometry_distance_traveled.py @@ -0,0 +1,108 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from math import sqrt +import rclpy +from rclpy.logging import get_logger +from nav_msgs.msg import Odometry +from py_trees.common import Status +import py_trees + + +class OdometryDistanceTraveled(py_trees.behaviour.Behaviour): + """ + Class to wait for a certain covered distance, based on odometry + """ + + def __init__(self, name, associated_actor, distance: float): + super().__init__(name) + self.namespace = associated_actor["namespace"] + self.distance_expected = distance + self.distance_traveled = 0. + self.previous_x = 0 + self.previous_y = 0 + self.first_run = True + self.logger = None + self.node = None + self.subscriber = None + self.callback_group = None + + def setup(self, **kwargs): + """ + Setup subscription and logger + """ + self.logger = get_logger('odometry_distance_traveled') + self.logger.debug(f"Waiting for traveled distance of {self.distance_expected}") + + try: + self.node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + self.callback_group = rclpy.callback_groups.MutuallyExclusiveCallbackGroup() + self.subscriber = self.node.create_subscription( + Odometry, self.namespace + '/odom', self._callback, 1000, callback_group=self.callback_group) + + def _callback(self, msg): + ''' + Subscriber callback + ''' + self.calculate_distance(msg) + + def calculate_distance(self, msg): + """ + Update the odometry distance + Args: + msg [Odometry]: current odometry message to update + """ + if self.first_run: + self.first_run = False + else: + x = msg.pose.pose.position.x + y = msg.pose.pose.position.y + d_increment = sqrt((x - self.previous_x) * (x - self.previous_x) + + (y - self.previous_y) * (y - self.previous_y)) + self.distance_traveled = self.distance_traveled + d_increment + self.logger.debug(f'Total distance traveled is {self.distance_traveled}m') + + self.previous_x = msg.pose.pose.position.x + self.previous_y = msg.pose.pose.position.y + + def initialise(self): + ''' + Initialize before ticking. + ''' + self.distance_traveled = 0. + self.previous_x = 0 + self.previous_y = 0 + self.first_run = True + + def update(self) -> py_trees.common.Status: + """ + Check if the traveled distance is reached + return: + py_trees.common.Status.SUCCESS if the distanced is reached, else + return py_trees.common.Status.RUNNING. + """ + + self.logger.debug(f"ticking: {self.distance_traveled}") + if self.distance_traveled >= self.distance_expected: + self.feedback_message = f"expected traveled distance reached: {self.distance_expected:.3}" # pylint: disable= attribute-defined-outside-init + return Status.SUCCESS + else: + self.feedback_message = f"distance traveled: {self.distance_traveled:.3} < {self.distance_expected:.3}" # pylint: disable= attribute-defined-outside-init + return Status.RUNNING diff --git a/scenario_execution/scenario_execution/actions/py_trees_ros_common.py b/scenario_execution/scenario_execution/actions/py_trees_ros_common.py new file mode 100644 index 00000000..2831f012 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/py_trees_ros_common.py @@ -0,0 +1,102 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# +# License: BSD +# https://raw.githubusercontent.com/splintered-reality/py_trees_ros/devel/LICENSE +# + +import py_trees +import py_trees_ros # pylint: disable=import-error +import typing +import rclpy.qos + + +class SubscriberHandler(py_trees_ros.subscribers.Handler): + """ + overrides Handler + """ + + def __init__(self, + name: str, + topic_name: str, + topic_type: typing.Any, + qos_profile: rclpy.qos.QoSProfile, + clearing_policy: py_trees.common.ClearingPolicy = py_trees.common.ClearingPolicy.ON_INITIALISE + ): + """ + override + """ + super(SubscriberHandler, self).__init__(name=name, topic_name=topic_name, + topic_type=topic_type, qos_profile=qos_profile, clearing_policy=clearing_policy) + + def setup(self, **kwargs): + """ + Initialises the subscriber. + """ + try: + self.node = kwargs['node'] # pylint: disable= attribute-defined-outside-init + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + self.subscriber = self.node.create_subscription( # pylint: disable= attribute-defined-outside-init + msg_type=self.topic_type, + topic=self.topic_name, + callback=self._callback, + qos_profile=self.qos_profile, + callback_group=rclpy.callback_groups.ReentrantCallbackGroup() + ) + + +class SubscriberWaitForData(SubscriberHandler): + """ + overrides WaitForData + """ + + def __init__(self, + name: str, + topic_name: str, + topic_type: typing.Any, + qos_profile: rclpy.qos.QoSProfile, + clearing_policy: py_trees.common.ClearingPolicy + ): + """ + overrides WaitForData + """ + super().__init__( + name=name, + topic_name=topic_name, + topic_type=topic_type, + qos_profile=qos_profile, + clearing_policy=clearing_policy + ) + + def update(self): + """ + Returns: + :class:`~py_trees.common.Status`: :attr:`~py_trees.common.Status.RUNNING` (no data) or :attr:`~py_trees.common.Status.SUCCESS` + """ + self.logger.debug("%s.update()]" % self.__class__.__name__) + with self.data_guard: + if self.msg is None: # pylint: disable= access-member-before-definition + self.feedback_message = "no message received yet" # pylint: disable= attribute-defined-outside-init + return py_trees.common.Status.RUNNING + else: + self.feedback_message = "got incoming" # pylint: disable= attribute-defined-outside-init + if self.clearing_policy == py_trees.common.ClearingPolicy.ON_SUCCESS: + self.msg = None # pylint: disable= attribute-defined-outside-init + return py_trees.common.Status.SUCCESS diff --git a/scenario_execution/scenario_execution/actions/ros_bag_record.py b/scenario_execution/scenario_execution/actions/ros_bag_record.py new file mode 100644 index 00000000..704c160e --- /dev/null +++ b/scenario_execution/scenario_execution/actions/ros_bag_record.py @@ -0,0 +1,149 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +from datetime import datetime +from enum import Enum + +import py_trees +from scenario_execution_base.actions import RunExternalProcess +import shutil +import signal + + +class RosBagRecordActionState(Enum): + """ + States for executing a ros bag recording + """ + WAITING_FOR_TOPICS = 1 + RECORDING = 2 + FAILURE = 5 + + +class RosBagRecord(RunExternalProcess): + """ + Class to execute ros bag recording + """ + + def __init__(self, name, destination_dir: str, topics: list, timestamp_suffix: bool, hidden_topics: bool, storage: str): + super().__init__(name) + if not isinstance(topics, list): + raise TypeError(f'Topics needs to be list of topics, got {type(topics)}.') + else: + self.topics = topics + self.destination_dir = destination_dir + self.timestamp_suffix = timestamp_suffix + self.include_hidden_topics = hidden_topics + self.current_state = RosBagRecordActionState.WAITING_FOR_TOPICS + self.bag_dir = '' + self.storage = storage + self.command = None + + def setup(self, **kwargs): + """ + set up + """ + if self.destination_dir: + if not os.path.exists(self.destination_dir): + raise ValueError( + f"Specified destination dir '{self.destination_dir}' does not exist") + self.bag_dir = self.destination_dir + '/' + self.bag_dir += self.get_scenario_name() + + if self.timestamp_suffix: + self.bag_dir += '_' + datetime.now().strftime("%Y%m%d%H%M%S") + + self.command = ["ros2", "bag", "record"] + if self.include_hidden_topics: + self.command.append("--include-hidden-topics") + if self.storage: + self.command.extend(["--storage", self.storage]) + + self.command.extend(["-o", self.bag_dir] + self.topics) + + self.logger.info(f'Command: {" ".join(self.command)}') + + def get_scenario_name(self): + """ + get scenario name from py tree root + """ + parent = self.parent + scenario_name = "INVALID" + while parent: + scenario_name = parent.name + parent = parent.parent + return scenario_name + + def get_logger_stderr(self): + """ + get logger for stderr messages + """ + return self.logger.info # ros2 bag record reports all messages on stderr + + def on_executed(self): + """ + Hook when process gets executed + """ + self.feedback_message = f"Waiting for all topics to subscribe..." # pylint: disable= attribute-defined-outside-init + self.current_state = RosBagRecordActionState.WAITING_FOR_TOPICS + + def check_running_process(self): + """ + hook to check running process + + return: + py_trees.common.Status + """ + if self.current_state == RosBagRecordActionState.WAITING_FOR_TOPICS: + while True: + try: + line = self.output.popleft() + if line.endswith('All requested topics are subscribed. Stopping discovery...'): + self.feedback_message = f"Recording..." # pylint: disable= attribute-defined-outside-init + self.current_state = RosBagRecordActionState.RECORDING + return py_trees.common.Status.SUCCESS + except IndexError: + break + return py_trees.common.Status.RUNNING + else: + self.current_state = RosBagRecordActionState.FAILURE + return py_trees.common.Status.FAILURE + + def cleanup(self): + """ + Cleanup on shutdown + """ + if self.current_state != RosBagRecordActionState.FAILURE: + self.logger.info('Waiting for process to quit...') + if self.process: + self.process.send_signal(signal.SIGINT) + self.process.wait() + self.logger.info('Process finished.') + if self.current_state == RosBagRecordActionState.WAITING_FOR_TOPICS and self.bag_dir and os.path.exists(self.bag_dir): + self.logger.info( + f'Cleanup while waiting for topics. Removing incomplete bag {self.bag_dir}...') + shutil.rmtree(self.bag_dir) + + def on_process_finished(self, ret): + """ + check result of process + + return: + py_trees.common.Status + """ + if self.current_state == RosBagRecordActionState.RECORDING: + self.current_state = RosBagRecordActionState.FAILURE + return py_trees.common.Status.FAILURE diff --git a/scenario_execution/scenario_execution/actions/ros_log_check.py b/scenario_execution/scenario_execution/actions/ros_log_check.py new file mode 100644 index 00000000..77c5d4b9 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/ros_log_check.py @@ -0,0 +1,82 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import py_trees +from rclpy.qos import QoSProfile, DurabilityPolicy, HistoryPolicy +import rclpy +from rcl_interfaces.msg import Log +from py_trees.common import Status + + +class RosLogCheck(py_trees.behaviour.Behaviour): + """ + Class for scanning the ros log for specific content + """ + + def __init__(self, name, values: list): + super().__init__(name) + if not isinstance(values, list): + raise TypeError(f'Value needs to be list of strings, got {type(values)}.') + else: + self.values = values + + self.subscriber = None + self.node = None + self.found = False + + def setup(self, **kwargs): + """ + Setup the subscriber + """ + try: + self.node: rclpy.Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + topic_qos = QoSProfile( + depth=100, + durability=DurabilityPolicy.TRANSIENT_LOCAL, + history=HistoryPolicy.KEEP_LAST) + + self.subscriber = self.node.create_subscription( # pylint: disable= attribute-defined-outside-init + msg_type=Log, + topic='/rosout', + callback=self._callback, + qos_profile=topic_qos, + callback_group=rclpy.callback_groups.ReentrantCallbackGroup() + ) + self.feedback_message = f"Waiting for log" # pylint: disable= attribute-defined-outside-init + + def update(self) -> py_trees.common.Status: + """ + Wait for specified log entries + + return: + py_trees.common.Status if found + """ + if self.found: + return Status.SUCCESS + else: + return Status.RUNNING + + def _callback(self, msg): + for val in self.values: + if val in msg.msg: + self.feedback_message = f"Found string '{val}' in '{msg.msg}'" # pylint: disable= attribute-defined-outside-init + self.found = True + break diff --git a/scenario_execution/scenario_execution/actions/ros_service_call.py b/scenario_execution/scenario_execution/actions/ros_service_call.py new file mode 100644 index 00000000..660af0f7 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/ros_service_call.py @@ -0,0 +1,121 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from ast import literal_eval +import importlib +from enum import Enum +from rclpy.node import Node +from rclpy.callback_groups import ReentrantCallbackGroup +from rosidl_runtime_py.set_message import set_message_fields +import py_trees # pylint: disable=import-error + + +class ServiceCallActionState(Enum): + """ + States for executing a service call + """ + IDLE = 1 + SERVICE_CALLED = 2 + DONE = 3 + ERROR = 4 + + +class RosServiceCall(py_trees.behaviour.Behaviour): + """ + ros service call behavior + """ + + def __init__(self, name, service_name: str, service_type: str, data: str): + super().__init__(name) + self.node = None + self.client = None + self.future = None + self.service_type = service_type + self.service_name = service_name + try: + trimmed_data = data.encode('utf-8').decode('unicode_escape') + self.data = literal_eval(trimmed_data) + except Exception as e: # pylint: disable=broad-except + raise ValueError(f"Error while parsing sevice call data:") from e + self.current_state = ServiceCallActionState.IDLE + self.cb_group = ReentrantCallbackGroup() + + def setup(self, **kwargs): + """ + Setup ROS2 node and service client + + """ + try: + self.node: Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + datatype_in_list = self.service_type.split(".") + self.service_type = getattr( + importlib.import_module(".".join(datatype_in_list[0:-1])), + datatype_in_list[-1]) + + self.client = self.node.create_client( + self.service_type, self.service_name, callback_group=self.cb_group) + + def update(self) -> py_trees.common.Status: + """ + Execute states + """ + self.logger.debug(f"Current State {self.current_state}") + result = py_trees.common.Status.FAILURE + if self.current_state == ServiceCallActionState.IDLE: + self.current_state = ServiceCallActionState.SERVICE_CALLED + if self.future: + self.future.cancel() + req = self.service_type.Request() + set_message_fields(req, self.data) + self.future = self.client.call_async(req) + self.future.add_done_callback(self.done_callback) + self.feedback_message = f"service request sent to {self.service_name}" # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.RUNNING + elif self.current_state == ServiceCallActionState.SERVICE_CALLED: + self.feedback_message = f"waiting for response from {self.service_name}" # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.RUNNING + elif self.current_state == ServiceCallActionState.DONE: + self.feedback_message = f"service response received" # pylint: disable= attribute-defined-outside-init + result = py_trees.common.Status.SUCCESS + else: + self.logger.error(f"Invalid state {self.current_state}") + + return result + + def done_callback(self, future): + """ + Callback function when the service future is done + """ + self.logger.debug(f"Received state {future.result()}") + if self.current_state == ServiceCallActionState.SERVICE_CALLED: + if self.check_response(future.result()): + self.current_state = ServiceCallActionState.DONE + else: + self.current_state = ServiceCallActionState.ERROR + else: + self.current_state = ServiceCallActionState.ERROR + + def check_response(self, _): + """ + Check the result of a service call + Can be overriden by subclasses + """ + return True diff --git a/scenario_execution/scenario_execution/actions/ros_set_node_parameter.py b/scenario_execution/scenario_execution/actions/ros_set_node_parameter.py new file mode 100644 index 00000000..4d2e9c79 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/ros_set_node_parameter.py @@ -0,0 +1,79 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from ast import literal_eval + +from .ros_service_call import RosServiceCall +from rcl_interfaces.msg import ParameterType + + +class RosSetNodeParameter(RosServiceCall): + """ + class for setting a node parameter + """ + + def __init__(self, name, node_name: str, parameter_name: str, parameter_value: str): + service_name = node_name + '/set_parameters' + if not service_name.startswith('/'): + service_name = '/' + service_name + + parameter_type = ParameterType.PARAMETER_STRING + parameter_assign_name = 'string_value' + if parameter_value.lower() == 'true' or parameter_value.lower() == 'false': + parameter_type = ParameterType.PARAMETER_BOOL + parameter_assign_name = 'bool_value' + elif parameter_value.isdigit(): + parameter_type = ParameterType.PARAMETER_INTEGER + parameter_assign_name = 'integer_value' + elif RosSetNodeParameter.is_float(parameter_value): + parameter_type = ParameterType.PARAMETER_DOUBLE + parameter_assign_name = 'double_value' + if parameter_value.startswith('['): + try: + vals = literal_eval(parameter_value) + except SyntaxError as e: + self.logger.error(f"Could not parse parameter_value: {parameter_value}: {e}") + pass + if vals: + if vals[0].lower() == 'true' or vals[0].lower() == 'false': + parameter_type = ParameterType.PARAMETER_BOOL_ARRAY + parameter_assign_name = 'bool_array_value' + elif vals[0].isdigit(): + parameter_type = ParameterType.PARAMETER_INTEGER_ARRAY + parameter_assign_name = 'integer_array_value' + elif RosSetNodeParameter.is_float(vals[0]): + parameter_type = ParameterType.PARAMETER_DOUBLE_ARRAY + parameter_assign_name = 'double_array_value' + else: + parameter_type = ParameterType.PARAMETER_STRING_ARRAY + parameter_assign_name = 'string_array_value' + + super().__init__(name, + service_name=service_name, + service_type='rcl_interfaces.srv.SetParameters', + data='{ "parameters": [{ "name": "' + parameter_name + '", "value": { "type": ' + str(parameter_type) + ', "' + parameter_assign_name + '": ' + parameter_value + '}}]}') + + @staticmethod + def is_float(element: any) -> bool: + """ + test for float type + """ + + try: + float(element) + return True + except ValueError: + return False diff --git a/scenario_execution/scenario_execution/actions/ros_topic_check_data.py b/scenario_execution/scenario_execution/actions/ros_topic_check_data.py new file mode 100644 index 00000000..1ebc6de7 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/ros_topic_check_data.py @@ -0,0 +1,56 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import importlib +import py_trees_ros # pylint: disable=import-error +from scenario_execution.actions.conversions import get_qos_preset_profile, \ + get_comparison_operator, get_clearing_policy + + +class RosTopicCheckData(py_trees_ros.subscribers.CheckData): + """ + Class to check if the message on ROS topic equals to the target message + """ + + def __init__(self, + name: str, + topic_name: str, + topic_type: str, + qos_profile: str, + variable_name: str, + expected_value: str, + comparison_operator: int, + fail_if_no_data: bool, + fail_if_bad_comparison: bool, + clearing_policy: int + ): + datatype_in_list = topic_type.split(".") + topic_type = getattr( + importlib.import_module(".".join(datatype_in_list[0:-1])), + datatype_in_list[-1] + ) + + super().__init__( + name=name, + topic_name=topic_name, + topic_type=topic_type, + qos_profile=get_qos_preset_profile(qos_profile), + variable_name=variable_name, + expected_value=expected_value, + comparison_operator=get_comparison_operator(comparison_operator), + fail_if_no_data=fail_if_no_data, + fail_if_bad_comparison=fail_if_bad_comparison, + clearing_policy=get_clearing_policy(clearing_policy)) diff --git a/scenario_execution/scenario_execution/actions/ros_topic_publish.py b/scenario_execution/scenario_execution/actions/ros_topic_publish.py new file mode 100644 index 00000000..2e8efda8 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/ros_topic_publish.py @@ -0,0 +1,85 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import importlib +from ast import literal_eval + +from rosidl_runtime_py.set_message import set_message_fields +from rclpy.node import Node + +import py_trees +from py_trees.common import Status + +from scenario_execution.actions.conversions import get_qos_preset_profile + + +class RosTopicPublish(py_trees.behaviour.Behaviour): + """ + class for publish a message on a ROS topic + """ + + def __init__(self, name, topic_type: str, topic_name: str, qos_profile: str, value: str + ): + super().__init__(name) + self.qos_profile = get_qos_preset_profile(qos_profile) + + # Parse message + datatype_in_list = topic_type.split(".") + self.topic_type = getattr( + importlib.import_module(".".join(datatype_in_list[0:-1])), + datatype_in_list[-1] + ) + self.topic_name = topic_name + self.msg_to_pub = self.topic_type() + parsed_value = literal_eval("".join(value.split('\\'))) + if not isinstance(parsed_value, dict): + raise TypeError(f'Parsed value needs type "dict", got {type(parsed_value)}.') + set_message_fields(self.msg_to_pub, parsed_value) + + # Initialize publisher and ROS node + self.publisher = None + self.node = None + + def setup(self, **kwargs): + """ + Setup the publisher + """ + try: + self.node: Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + try: + self.publisher = self.node.create_publisher( + msg_type=self.topic_type, + topic=self.topic_name, + qos_profile=self.qos_profile + ) + except TypeError as e: + raise TypeError(f"{self.name}") from e + + def update(self) -> py_trees.common.Status: + """ + Publish the msg to topic + + return: + py_trees.common.Status if published + """ + self.publisher.publish(self.msg_to_pub) + self.feedback_message = f"published {self.msg_to_pub}" # pylint: disable= attribute-defined-outside-init + return Status.SUCCESS diff --git a/scenario_execution/scenario_execution/actions/ros_topic_wait_for_data.py b/scenario_execution/scenario_execution/actions/ros_topic_wait_for_data.py new file mode 100644 index 00000000..e50aa10a --- /dev/null +++ b/scenario_execution/scenario_execution/actions/ros_topic_wait_for_data.py @@ -0,0 +1,45 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import importlib +from scenario_execution.actions.conversions import get_qos_preset_profile, get_clearing_policy +from .py_trees_ros_common import SubscriberWaitForData + + +class RosTopicWaitForData(SubscriberWaitForData): + """ + Class to check if the message on ROS topic equals to the target message + + Args: + topic_name[str]: name of the topic to connect to + topic_type[str]: class of the message type (e.g. std_msgs.msg.String) + qos_profile[str]: qos profile for the subscriber + clearing_policy[str]: when to clear the data + """ + + def __init__(self, name: str, topic_name: str, topic_type: str, qos_profile: str, clearing_policy: str): + datatype_in_list = topic_type.split(".") + topic_type = getattr( + importlib.import_module(".".join(datatype_in_list[0:-1])), + datatype_in_list[-1] + ) + + super().__init__( + name=name, + topic_name=topic_name, + topic_type=topic_type, + qos_profile=get_qos_preset_profile(qos_profile), + clearing_policy=get_clearing_policy(clearing_policy)) diff --git a/scenario_execution/scenario_execution/actions/ros_topic_wait_for_topics.py b/scenario_execution/scenario_execution/actions/ros_topic_wait_for_topics.py new file mode 100644 index 00000000..5c985720 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/ros_topic_wait_for_topics.py @@ -0,0 +1,58 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import py_trees +from rclpy.node import Node + + +class RosTopicWaitForTopics(py_trees.behaviour.Behaviour): + """ + Class to check if ROS topic are available + """ + + def __init__(self, topics: list): + super().__init__('RosTopicWaitForTopics') + if not isinstance(topics, list): + raise TypeError(f'Topics needs to be list of topics, got {type(topics)}.') + else: + self.topics = topics + self.node = None + + def setup(self, **kwargs): + """ + Setup the publisher + """ + try: + self.node: Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + def update(self) -> py_trees.common.Status: + """ + Publish the msg to topic + + return: + py_trees.common.Status if published + """ + available_topics = self.node.get_topic_names_and_types() + available_topics = [seq[0] for seq in available_topics] + result = all(elem in available_topics for elem in self.topics) + if result: + return py_trees.common.Status.SUCCESS + else: + return py_trees.common.Status.RUNNING diff --git a/scenario_execution/scenario_execution/actions/tf_close_to.py b/scenario_execution/scenario_execution/actions/tf_close_to.py new file mode 100644 index 00000000..7912a1f1 --- /dev/null +++ b/scenario_execution/scenario_execution/actions/tf_close_to.py @@ -0,0 +1,198 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from math import sqrt + +import rclpy +from rclpy.node import Node +import py_trees +from py_trees.common import Status +from visualization_msgs.msg import Marker +from geometry_msgs.msg import PoseStamped + +from tf2_ros.buffer import Buffer +from tf2_ros import TransformException # pylint: disable= no-name-in-module + +from scenario_execution.actions.nav2_common import NamespacedTransformListener + + +class TfCloseTo(py_trees.behaviour.Behaviour): + """ + class for distance condition in ROS Gazebo simulation + """ + + def __init__( + self, + name, + associated_actor: dict, + namespace_override: str, + reference_point, + threshold: float, + sim: bool, + robot_frame_id: str, + ): + super().__init__(name) + + if not reference_point: + raise TypeError(f'reference_point not initialized.') + if not threshold: + raise TypeError(f'threshold not initialized.') + + self.namespace = associated_actor["namespace"] + if namespace_override: + self.namespace = namespace_override + self.reference_point = reference_point + self.threshold = threshold + self.sim = sim + + if robot_frame_id: + self.robot_frame_id = robot_frame_id + else: + self.robot_frame_id = 'base_link' + + self.node = None + self.marker_handler = None + self.marker_id = None + self.tf_buffer = None + self.tf_listener = None + self.success = False + + def setup(self, **kwargs): + """ + Setup ROS node handle and subscriber before ticking + """ + try: + self.node: Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + try: + self.marker_handler = kwargs['marker_handler'] + except KeyError as e: + error_message = "didn't find 'marker_handler' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__ + ) + raise KeyError(error_message) from e + self.feedback_message = f"Waiting for transform map --> base_link" # pylint: disable= attribute-defined-outside-init + self.tf_buffer = Buffer() + tf_prefix = self.namespace + if not tf_prefix.startswith('/') and tf_prefix != '': + tf_prefix = "/" + tf_prefix + self.tf_listener = NamespacedTransformListener( + node=self.node, + buffer=self.tf_buffer, + tf_topic=(tf_prefix + "/tf"), + tf_static_topic=(tf_prefix + "/tf_static"), + ) + + marker = Marker() + marker.header.frame_id = 'map' + marker.type = Marker.CYLINDER + marker.scale.x = self.threshold + marker.scale.y = self.threshold + marker.scale.z = 0.01 + marker.color.a = 1.0 + marker.color.r = 0.5 + marker.color.g = 0.5 + marker.color.b = 0.5 + marker.pose.position.x = self.reference_point['x'] + marker.pose.position.y = self.reference_point['y'] + marker.pose.position.z = 0.0 + self.marker_id = self.marker_handler.add_marker(marker) + + def update(self) -> py_trees.common.Status: + """ + Check if the subscriber already received the right msg while ticking + """ + robot_pose, success = self.get_robot_pose_from_tf() + if not success: + self.feedback_message = f"the pose of {self.namespace} could not be retrieved from tf" # pylint: disable= attribute-defined-outside-init + return Status.RUNNING + dist = self.euclidean_dist(robot_pose.pose.position) + marker = self.marker_handler.get_marker(self.marker_id) + self.success = dist <= self.threshold + if self.success: + self.feedback_message = f"{self.namespace} reached point." # pylint: disable= attribute-defined-outside-init + marker.color.r = 0.0 + marker.color.g = 1.0 + marker.color.b = 0.0 + self.marker_handler.update_marker(self.marker_id, marker) + return Status.SUCCESS + else: + self.feedback_message = f"{self.namespace} has not reached point (distance={dist-self.threshold:.2f})" # pylint: disable= attribute-defined-outside-init + marker.color.r = 1.0 + marker.color.g = 1.0 + marker.color.b = 0.0 + self.marker_handler.update_marker(self.marker_id, marker) + return Status.RUNNING + + def get_robot_pose_from_tf(self): + ''' + function to get pose of the robot (i.e., the base_link frame) with + respect to the map frame via tf + + returns: + pose: pose of the robot in the map frame + ''' + pose = PoseStamped() + when = self.node.get_clock().now() + if self.sim: + when = rclpy.time.Time() + try: + t = self.tf_buffer.lookup_transform( + 'map', + self.robot_frame_id, + when, + timeout=rclpy.duration.Duration(seconds=1.0), + ) + self.feedback_message = f"Transform map -> base_link got available." # pylint: disable= attribute-defined-outside-init + except TransformException as ex: + self.feedback_message = f"Could not transform map to base_link" # pylint: disable= attribute-defined-outside-init + self.node.get_logger().warn( + f'Could not transform map to base_link at time {when}: {ex}') + return pose, False + + pose.header = t.header + pose.header.frame_id = 'map' + + pose.pose.position.x = t.transform.translation.x + pose.pose.position.y = t.transform.translation.y + pose.pose.orientation.x = t.transform.rotation.x + pose.pose.orientation.y = t.transform.rotation.y + pose.pose.orientation.z = t.transform.rotation.z + pose.pose.orientation.w = t.transform.rotation.w + + return pose, True + + def euclidean_dist(self, pos): + ''' + Calculate the euclidean distance between the robot position and the reference point + + Args: + pos: Position of the robot + + return: + Euclidean distance in float + ''' + return sqrt((self.reference_point['x'] - pos.x) ** 2 + (self.reference_point['y'] - pos.y) ** 2) + + def cleanup(self): + """ + Cleanup on shutdown + """ + self.marker_handler.remove_markers([self.marker_id]) diff --git a/scenario_execution/scenario_execution/get_osc_library.py b/scenario_execution/scenario_execution/get_osc_library.py new file mode 100644 index 00000000..91b46520 --- /dev/null +++ b/scenario_execution/scenario_execution/get_osc_library.py @@ -0,0 +1,19 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +def get_ros_library(): + return 'scenario_execution', 'ros.osc' diff --git a/scenario_execution/scenario_execution/lib_osc/ros.osc b/scenario_execution/scenario_execution/lib_osc/ros.osc new file mode 100644 index 00000000..d57de868 --- /dev/null +++ b/scenario_execution/scenario_execution/lib_osc/ros.osc @@ -0,0 +1,135 @@ +import osc.robotics + +# Policy rules for behaviours to dictate when data should be cleared/reset. +# on_initialise Clear when entering the py_trees.behaviour.Behaviour.initialise method. +# on_success Clear when returning py_trees.common.Status.SUCCESS +# never Never clear the data +enum clearing_policy: [ + on_initialise, + on_success, + never +] + +enum qos_preset_profiles: [ + parameters, + parameter_events, + sensor_data, + services_default, + system_default +] + +enum comparison_operator: [ + lt, + le, + eq, + ne, + ge, + gt +] + +action wait_for_data: + topic_name: string # name of the topic to connect to + topic_type: string # class of the message type (e.g. std_msgs.msg.String) + qos_profile: qos_preset_profiles = qos_preset_profiles!system_default # qos profile for the subscriber + clearing_policy: clearing_policy = clearing_policy!on_initialise # when to clear the data + +action wait_for_topics: + # wait for topics to get available + topics: list of topics + +action check_data: + topic_name: string # name of the topic to connect to + topic_type: string # class of the message type (e.g. std_msgs.msg.String) + qos_profile: qos_preset_profiles = qos_preset_profiles!system_default # qos profile for the subscriber + variable_name: string # name of the variable to check + expected_value: string # expected value of the variable + comparison_operator: comparison_operator = comparison_operator!eq # one from the python `operator module`_ + fail_if_no_data: bool = false # py_trees.common.Status.FAILURE instead of py_trees.common.Status.RUNNING if there is no data yet + fail_if_bad_comparison: bool = true # py_trees.common.Status.FAILURE instead of py_trees.common.Status.RUNNING if comparison failed + clearing_policy: clearing_policy = clearing_policy!on_initialise # when to clear the data + +action topic_to_blackboard: + topic_name: string # name of the topic to connect to + topic_type: string # class of the message type (e.g. std_msgs.msg.String) + qos_profile: qos_preset_profiles = qos_preset_profiles!system_default # qos profile for the subscriber + blackboard_variables: string # blackboard variable string or dict (names (keys) - message subfields (values)), use a value of None to indicate the entire message + initialise_variables: string = '{}' # initialise the blackboard variables to some defaults + clearing_policy: clearing_policy = clearing_policy!on_initialise # when to clear the data + +action event_to_blackboard: + topic_name: string # name of the topic to connect to + qos_profile: qos_preset_profiles = qos_preset_profiles!system_default # qos profile for the subscriber + variable_name: string # name of the variable to check + +action topic_from_blackboard: + topic_name: string # name of the topic to connect to + topic_type: string # class of the message type (e.g. std_msgs.msg.String) + qos_profile: qos_preset_profiles = qos_preset_profiles!system_default # qos profile for the publisher + blackboard_variable: string # name of the variable on the blackboard (can be nested) + +action service_call: + service_name: string # name of the service to connect to + service_type: string # class of the message type (e.g. std_srvs.msg.Empty) + data: string # call content + +action wait_for_blackboard_variable: + variable_name: string # name of the service to connect to + +action set_blackboard_variable: + variable_name: string # name of the blackboard variable + variable_value: string # value of the variable to set + +action unset_blackboard_variable: + key: string # name of the blackboard variable + +action topic_publish: + topic_name: string # name of the topic to connect to + topic_type: string # class of the message type (e.g. std_msgs.msg.String) + qos_profile: qos_preset_profiles = qos_preset_profiles!system_default # qos profile for the subscriber + value: string # value of the published topic + +action set_node_parameter: + node_name: string # name of the node + parameter_name: string # name of the parameter + parameter_value: string # new value of the parameter + +action record_bag: + # Record a dataset + destination_dir: string = '' # If destination dir is empty, the current directory is used + topics: list of topics + timestamp_suffix: bool = true# Add a timestamp suffix to output directory name + hidden_topics: bool = false # whether to record hidden topics + storage: string = '' # storage type to use (empty string: use default) + +action log_check: + # Check the log for specific output + values: list of string # string to check for. If found, action succeeds + +action differential_drive_robot.init_nav2: + initial_pose: pose_3d + base_frame_id: string = 'base_link' + use_initial_pose: bool = true # if false, no initial_pose is needed (useful when using slam instead of amcl for localization) + namespace_override: string = '' # if set, it's used as namespace (instead of the associated actor's name) + wait_for_initial_pose: bool = false # if true the initial pose needs to be set externally (e.g. manually through rviz) + +action differential_drive_robot.nav_to_pose: + goal_pose: pose_3d + namespace_override: string = '' # if set, it's used as namespace (instead of the associated actor's name) + action_topic: string = 'navigate_to_pose' # Name of action + monitor_progress: bool = true # if yes, the action returns after the goal is reached or on failure. If no, the action returns after request. + +action differential_drive_robot.nav_through_poses: + goal_poses: list of pose_3d + namespace_override: string = '' # if set, it's used as namespace (instead of the associated actor's name) + monitor_progress: bool = true # if yes, the action returns after the goal is reached or on failure. If no, the action returns after request. + +action differential_drive_robot.tf_close_to: + namespace_override: string = '' # if set, it's used as namespace (instead of the associated actor's name) + reference_point: position_3d # z is not considered + threshold: length + sim: bool = false # in simulation, we need to look up the transform map --> base_link at a different time as the scenario execution node is not allowed to use the sim time + robot_frame_id: string = 'base_link' # defines the TF frame id of the robot + +action differential_drive_robot.odometry_distance_traveled: + namespace: string = '' + distance: length diff --git a/scenario_execution/scenario_execution/logging_ros.py b/scenario_execution/scenario_execution/logging_ros.py new file mode 100644 index 00000000..88f67278 --- /dev/null +++ b/scenario_execution/scenario_execution/logging_ros.py @@ -0,0 +1,69 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Logger Class for ROS""" + +import rclpy +from scenario_execution_base.utils.logging import BaseLogger + + +class RosLogger(BaseLogger): + """ + Class for logger for ROS scenario execution + + Args: + name [str]: name of the logger + """ + + def __init__(self, name: str): + super().__init__(name) + self.logger = rclpy.logging.get_logger(name) + + def info(self, msg: str): + """ + Log info in ROS2 + + Args: + msg [str]: msg to print + """ + self.logger.info(msg) + + def debug(self, msg: str): + """ + Log debug info in ROS2 + + Args: + msg [str]: msg to print + """ + self.logger.debug(msg) + + def warning(self, msg: str): + """ + Log warning in ROS2 + + Args: + msg [str]: msg to print + """ + self.logger.warning(msg) + + def error(self, msg: str): + """ + Log error in ROS2 + + Args: + msg [str]: msg to print + """ + self.logger.error(msg) diff --git a/scenario_execution/scenario_execution/marker_handler.py b/scenario_execution/scenario_execution/marker_handler.py new file mode 100644 index 00000000..dcf14432 --- /dev/null +++ b/scenario_execution/scenario_execution/marker_handler.py @@ -0,0 +1,60 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Marker handler""" + +from visualization_msgs.msg import Marker + + +class MarkerHandler(object): + """ + Class for managing markers + + Args: + name [str]: name of the logger + """ + + def __init__(self, node): + self.node = node + self.markers = [] + self.marker_publisher = self.node.create_publisher(Marker, '/scenario_marker', 5) + + def add_marker(self, marker: Marker): + marker.header.frame_id = 'map' + marker.action = marker.ADD + self.markers.append(marker) + + self.marker_publisher.publish(marker) + + return self.markers.index(marker) + + def get_marker(self, marker_id): + return self.markers[marker_id] + + def remove_markers(self, marker_id_list): + for marker_id in marker_id_list: + if marker_id is not None and marker_id < len(self.markers): + marker = self.markers[marker_id] + marker.action = marker.DELETE + self.marker_publisher.publish(marker) + + def update_marker(self, marker_id, marker): + self.markers[marker_id] = marker + self.markers[marker_id].id = marker_id + self.markers[marker_id].header.frame_id = 'map' + self.markers[marker_id].action = marker.MODIFY + + self.marker_publisher.publish(self.markers[marker_id]) diff --git a/scenario_execution/scenario_execution/scenario_execution_ros.py b/scenario_execution/scenario_execution/scenario_execution_ros.py new file mode 100644 index 00000000..04cfec7b --- /dev/null +++ b/scenario_execution/scenario_execution/scenario_execution_ros.py @@ -0,0 +1,158 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Main entry for scenario_execution_ros """ +import sys +from datetime import datetime +import rclpy # pylint: disable=import-error +import py_trees_ros # pylint: disable=import-error +from scenario_execution_base import ScenarioExecution +from .logging_ros import RosLogger +from .marker_handler import MarkerHandler + + +class ROSScenarioExecution(ScenarioExecution): + """ + Class for scenario execution using ROS2 as middleware + """ + + def __init__(self) -> None: + self.node = rclpy.create_node(node_name="scenario_execution") + + # parse from commandline + args_without_ros = rclpy.utilities.remove_ros_args(sys.argv[1:]) + args = ScenarioExecution.parse_args(args_without_ros) + debug = args.debug + log_model = args.log_model + live_tree = args.live_tree + scenario = args.scenario + test_output = args.test_output + + # override commandline by ros parameters + self.node.declare_parameter('debug', False) + self.node.declare_parameter('log_model', False) + self.node.declare_parameter('live_tree', False) + self.node.declare_parameter('test_output', "") + self.node.declare_parameter('scenario', "") + + if self.node.get_parameter('debug').value: + debug = self.node.get_parameter('debug').value + if self.node.get_parameter('log_model').value: + log_model = self.node.get_parameter('log_model').value + if self.node.get_parameter('live_tree').value: + live_tree = self.node.get_parameter('live_tree').value + if self.node.get_parameter('scenario').value: + scenario = self.node.get_parameter('scenario').value + if self.node.get_parameter('test_output').value: + test_output = self.node.get_parameter('test_output').value + super().__init__(debug=debug, log_model=log_model, live_tree=live_tree, scenario=scenario, test_output=test_output) + + def _get_logger(self): + """ + Get a logger from ROS2 with name "scenario_execution" + Overriden parent class method + """ + return RosLogger('scenario_execution') + + def setup_behaviour_tree(self, tree): + """ + Setup the behaviour tree + Using py_trees_ros to get a node handle on ROS2 and tick in syn with ROS2 + + Args: + tree [py_trees.behaviour.Behaviour]: root of the behaviour tree + + return: + py_trees_ros.trees.BehaviourTree + """ + return py_trees_ros.trees.BehaviourTree(tree) + + def run(self) -> bool: + """ + Setup behaviour tree and run ROS node + + return: + True if all scenarios are executed successfully + """ + executor = rclpy.executors.MultiThreadedExecutor() + marker_handler = MarkerHandler(self.node) + executor.add_node(self.node) + + if not self.scenarios: + self.logger.info("No scenarios to execute.") + + failure = False + for tree in self.scenarios: + self.logger.info(f"Executing scenario '{tree.name}'") + start = datetime.now() + if not tree: + self.logger.error(f'Scenario {tree.name} has no executables.') + failure = True + continue + + result = self.setup(tree, node=self.node, marker_handler=marker_handler) + if not result: + failure = True + + if result: + self.behaviour_tree.tick_tock(period_ms=1000. * self.tick_tock_period) + + # Spin ROS node + while rclpy.ok() and not self.shutdown_requested: + try: + # rclpy.spin_once(self.behaviour_tree.node) + executor.spin_once() + except KeyboardInterrupt: + self.blackboard.fail = True + break + + self.behaviour_tree.interrupt() + failure = failure or self.blackboard.fail + result = not self.blackboard.fail + if result: + self.logger.info(f"Scenario '{tree.name}' succeeded.") + else: + self.logger.error(f"Scenario '{tree.name}' failed.") + self.logger.error(self.last_snapshot_visitor.last_snapshot) + + self.add_result((tree.name, self.blackboard.fail, "execution failed", + self.last_snapshot_visitor.last_snapshot, datetime.now() - start)) + self.cleanup_behaviours(tree) + self.behaviour_tree.shutdown() + + return not failure + + +def main(): + """ + main function + """ + rclpy.init(args=sys.argv) + ros_scenario_execution = ROSScenarioExecution() + result = ros_scenario_execution.parse() + + if result: + result = ros_scenario_execution.run() + rclpy.shutdown() + ros_scenario_execution.report_results() + if result: + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/scenario_execution/scenarios/test/test_ros_service_call.osc b/scenario_execution/scenarios/test/test_ros_service_call.osc new file mode 100644 index 00000000..b68007c7 --- /dev/null +++ b/scenario_execution/scenarios/test/test_ros_service_call.osc @@ -0,0 +1,14 @@ +import osc.ros + +scenario test_ros_service_call: + do parallel: + test: serial: + service_call() with: + keep(it.service_name == '/bla') + keep(it.service_type == 'std_srvs.srv.SetBool') + keep(it.data == '{\"data\": True}') + wait elapsed(2s) + emit_arrival: emit end + time_out: serial: + wait_for_30s: wait elapsed(30s) + time_out_shutdown: emit fail diff --git a/scenario_execution/scenarios/test/test_ros_service_call_blocking.osc b/scenario_execution/scenarios/test/test_ros_service_call_blocking.osc new file mode 100644 index 00000000..64e54412 --- /dev/null +++ b/scenario_execution/scenarios/test/test_ros_service_call_blocking.osc @@ -0,0 +1,74 @@ +import osc.ros + +scenario test_ros_service_call_blocking: + do parallel: + test: serial: + service_call() with: + keep(it.service_name == '/bla_service') + keep(it.service_type == 'std_srvs.srv.SetBool') + keep(it.data == '{\"data\": True}') + wait elapsed(10s) + emit end + test2: serial: + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 0}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 1}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 2}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 3}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 4}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 5}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 6}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 7}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 8}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 9}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 10}') + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Int32') + keep(it.value == '{\"data\": 11}') + time_out: serial: + wait_for_30s: wait elapsed(30s) + time_out_shutdown: emit fail diff --git a/scenario_execution/scenarios/test/test_ros_set_node_parameter.osc b/scenario_execution/scenarios/test/test_ros_set_node_parameter.osc new file mode 100644 index 00000000..e9b621d3 --- /dev/null +++ b/scenario_execution/scenarios/test/test_ros_set_node_parameter.osc @@ -0,0 +1,18 @@ +import osc.ros + +scenario test_ros_set_node_parameter: + do parallel: + test: serial: + set_node_parameter() with: + keep(it.node_name == '/test_node') + keep(it.parameter_name == 'testFloatParam') + keep(it.parameter_value == '3.14') + second_call: set_node_parameter() with: + keep(it.node_name == '/test_node') + keep(it.parameter_name == 'testBoolParam') + keep(it.parameter_value == 'True') + wait elapsed(3s) + emit end + time_out: serial: + wait elapsed(10s) + time_out_shutdown: emit fail diff --git a/scenario_execution/scenarios/test/test_ros_topic_check_data.osc b/scenario_execution/scenarios/test/test_ros_topic_check_data.osc new file mode 100644 index 00000000..2a3c0038 --- /dev/null +++ b/scenario_execution/scenarios/test/test_ros_topic_check_data.osc @@ -0,0 +1,16 @@ +import osc.ros + +scenario test_ros_topic_check_data: + do parallel: + test: serial: + check_data() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.String') + keep(it.clearing_policy == on_initialise) + keep(it.variable_name == 'data') + keep(it.expected_value == 'hallo') + keep(it.comparison_operator == eq) + emit_arrival: emit end + time_out: serial: + wait_for_30s: wait elapsed(30s) + time_out_shutdown: emit fail diff --git a/scenario_execution/scenarios/test/test_ros_topic_publish.osc b/scenario_execution/scenarios/test/test_ros_topic_publish.osc new file mode 100644 index 00000000..d7eede99 --- /dev/null +++ b/scenario_execution/scenarios/test/test_ros_topic_publish.osc @@ -0,0 +1,13 @@ +import osc.ros + +scenario test_ros_topic_publish: + do parallel: + test: serial: + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Bool') + keep(it.value == '{\"data\": True}') + emit end + time_out: serial: + wait elapsed(10s) + emit fail diff --git a/scenario_execution/scenarios/test/test_ros_topic_wait_for_data.osc b/scenario_execution/scenarios/test/test_ros_topic_wait_for_data.osc new file mode 100644 index 00000000..6d58058c --- /dev/null +++ b/scenario_execution/scenarios/test/test_ros_topic_wait_for_data.osc @@ -0,0 +1,13 @@ +import osc.ros + +scenario test_ros_topic_wait_for_data: + do parallel: + test: serial: + wait_for_data() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Empty') + keep(it.clearing_policy == on_initialise) + emit_arrival: emit end + time_out: serial: + wait_for_30s: wait elapsed(30s) + time_out_shutdown: emit fail diff --git a/scenario_execution/setup.cfg b/scenario_execution/setup.cfg new file mode 100644 index 00000000..f0a819ee --- /dev/null +++ b/scenario_execution/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/scenario_execution +[install] +install_scripts=$base/lib/scenario_execution diff --git a/scenario_execution/setup.py b/scenario_execution/setup.py new file mode 100644 index 00000000..21ca4314 --- /dev/null +++ b/scenario_execution/setup.py @@ -0,0 +1,69 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Setup python package """ +from glob import glob +import os +from setuptools import find_packages, setup + +PACKAGE_NAME = 'scenario_execution' + +setup( + name=PACKAGE_NAME, + version='1.0.0', + packages=find_packages(), + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + PACKAGE_NAME]), + ('share/' + PACKAGE_NAME, ['package.xml']), + (os.path.join('share', PACKAGE_NAME, 'scenarios'), glob('scenarios/*.osc')), + (os.path.join('share', PACKAGE_NAME, 'scenarios', 'test'), glob('scenarios/test/*osc')), + (os.path.join('share', PACKAGE_NAME, 'launch'), glob('launch/*launch.py')) + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Scenario Execution for ROS', + license='Apache License 2.0', + tests_require=['pytest'], + include_package_data=True, + entry_points={ + 'console_scripts': [ + 'scenario_execution = scenario_execution.scenario_execution_ros:main', + ], + 'scenario_execution.actions': [ + 'differential_drive_robot.init_nav2 = scenario_execution.actions.init_nav2:InitNav2', + 'differential_drive_robot.nav_to_pose = scenario_execution.actions.nav_to_pose:NavToPose', + 'differential_drive_robot.nav_through_poses = scenario_execution.actions.nav_through_poses:NavThroughPoses', + 'wait_for_data = scenario_execution.actions.ros_topic_wait_for_data:RosTopicWaitForData', + 'check_data = scenario_execution.actions.ros_topic_check_data:RosTopicCheckData', + 'service_call = scenario_execution.actions.ros_service_call:RosServiceCall', + 'topic_publish = scenario_execution.actions.ros_topic_publish:RosTopicPublish', + 'odometry_distance_traveled = ' + 'scenario_execution.actions.odometry_distance_traveled:OdometryDistanceTraveled', + 'set_node_parameter = scenario_execution.actions.ros_set_node_parameter:RosSetNodeParameter', + 'record_bag = scenario_execution.actions.ros_bag_record:RosBagRecord', + 'wait_for_topics = scenario_execution.actions.ros_topic_wait_for_topics:RosTopicWaitForTopics', + 'log_check = scenario_execution.actions.ros_log_check:RosLogCheck', + 'differential_drive_robot.tf_close_to = scenario_execution.actions.tf_close_to:TfCloseTo', + ], + 'scenario_execution.osc_libraries': [ + 'ros = ' + 'scenario_execution.get_osc_library:get_ros_library', + ] + }, +) diff --git a/scenario_execution/test/test_ros_publish_receive.py b/scenario_execution/test/test_ros_publish_receive.py new file mode 100644 index 00000000..b32515c8 --- /dev/null +++ b/scenario_execution/test/test_ros_publish_receive.py @@ -0,0 +1,72 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import unittest +import rclpy +from scenario_execution import ROSScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestScenarioExectionSuccess(unittest.TestCase): + # pylint: disable=missing-function-docstring + + @classmethod + def setUpClass(cls): + rclpy.init() + + @classmethod + def tearDownClass(cls): + rclpy.shutdown() + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ROSScenarioExecution() + + def test_success(self): + scenario_content = """ +import osc.ros + +scenario test_ros_topic_publish: + do serial: + parallel: + test: serial: + wait elapsed(1s) + topic_publish() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Bool') + keep(it.value == '{\\\"data\\\": True}') + receive: serial: + wait_for_data() with: + keep(it.topic_name == '/bla') + keep(it.topic_type == 'std_msgs.msg.Bool') + keep(it.clearing_policy == clearing_policy!on_initialise) + emit end + time_out: serial: + wait elapsed(10s) + emit fail +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) diff --git a/scenario_execution/test/test_ros_service_call.py b/scenario_execution/test/test_ros_service_call.py new file mode 100644 index 00000000..e6733846 --- /dev/null +++ b/scenario_execution/test/test_ros_service_call.py @@ -0,0 +1,71 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import unittest +import rclpy +import threading +from scenario_execution import ROSScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from ament_index_python.packages import get_package_share_directory + +from std_srvs.srv import SetBool + +os.environ["PYTHONUNBUFFERED"] = '1' + + +class TestScenarioExectionSuccess(unittest.TestCase): + # pylint: disable=missing-function-docstring + + @classmethod + def setUpClass(cls): + rclpy.init() + + @classmethod + def tearDownClass(cls): + rclpy.shutdown() + + def setUp(self): + self.request_received = None + self.node = rclpy.create_node('test_node') + + self.executor = rclpy.executors.MultiThreadedExecutor() + self.executor.add_node(self.node) + self.executor_thread = threading.Thread(target=self.executor.spin, daemon=True) + self.executor_thread.start() + + self.scenario_dir = get_package_share_directory('scenario_execution') + + self.srv = self.node.create_service(SetBool, "/bla", self.service_callback) + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ROSScenarioExecution() + + def tearDown(self): + self.node.destroy_node() + + def service_callback(self, msg, response): + self.request_received = msg.data + return response + + def test_success(self): + scenarios = self.parser.process_file(os.path.join( + self.scenario_dir, 'scenarios', 'test', 'test_ros_service_call.osc'), False) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) + self.assertTrue(self.request_received) diff --git a/scenario_execution/test/test_ros_service_call_blocking.py b/scenario_execution/test/test_ros_service_call_blocking.py new file mode 100644 index 00000000..f2a85e86 --- /dev/null +++ b/scenario_execution/test/test_ros_service_call_blocking.py @@ -0,0 +1,98 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +import os +import time +import unittest +import rclpy +import threading +from scenario_execution import ROSScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from ament_index_python.packages import get_package_share_directory + +from std_srvs.srv import SetBool +from std_msgs.msg import Int32 + +os.environ["PYTHONUNBUFFERED"] = '1' + + +class TestScenarioExectionSuccess(unittest.TestCase): + # pylint: disable=missing-function-docstring + + @classmethod + def setUpClass(cls): + rclpy.init() + + @classmethod + def tearDownClass(cls): + rclpy.shutdown() + + def setUp(self): + self.received_msgs = [] + self.node = rclpy.create_node('test_node') + + self.executor = rclpy.executors.MultiThreadedExecutor() + self.executor.add_node(self.node) + self.executor_thread = threading.Thread(target=self.executor.spin, daemon=True) + self.executor_thread.start() + + self.callback_group = rclpy.callback_groups.ReentrantCallbackGroup() + self.srv = self.node.create_service( + SetBool, "/bla_service", self.service_callback, callback_group=self.callback_group) + self.sub = self.node.create_subscription( + Int32, "/bla", self.topic_callback, 10, callback_group=self.callback_group) + + self.scenario_dir = get_package_share_directory('scenario_execution') + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ROSScenarioExecution() + + def tearDown(self): + self.node.destroy_node() + + def service_callback(self, msg, response): + self.assertTrue(msg.data, "Invalid request received") + current_time = self.node.get_clock().now() + end_time = current_time + rclpy.duration.Duration(seconds=5) + while current_time <= end_time: + time.sleep(0.1) + current_time = self.node.get_clock().now() + return response + + def topic_callback(self, msg): + current_time = self.node.get_clock().now() + # print(f"Received {msg}") + # if self.received_msgs: + # print(f"Since last: {current_time - self.received_msgs[-1][0]}") + self.received_msgs.append((current_time, msg)) + + def test_success(self): + scenarios = self.parser.process_file(os.path.join( + self.scenario_dir, 'scenarios', 'test', 'test_ros_service_call_blocking.osc'), False) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) + + self.assertGreater(len(self.received_msgs), 0) + prev_elem = self.received_msgs[0] + for elem in self.received_msgs[1:]: + self.assertGreater(elem[1].data, prev_elem[1].data) + time_since_last = elem[0]-prev_elem[0] + self.assertGreaterEqual(time_since_last, rclpy.duration.Duration(seconds=0.8)) + self.assertLessEqual(time_since_last, rclpy.duration.Duration(seconds=1.6)) + prev_elem = elem diff --git a/scenario_execution/test/test_ros_set_node_parameter.py b/scenario_execution/test/test_ros_set_node_parameter.py new file mode 100644 index 00000000..84a61961 --- /dev/null +++ b/scenario_execution/test/test_ros_set_node_parameter.py @@ -0,0 +1,81 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Test for spawn and exists """ +import os +import unittest +import threading + +from ament_index_python.packages import get_package_share_directory + +import rclpy +from rcl_interfaces.msg import SetParametersResult + +from scenario_execution import ROSScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger + + +class TestRosSetNodeParameter(unittest.TestCase): + # pylint: disable=missing-function-docstring,missing-class-docstring + @classmethod + def setUpClass(cls): + rclpy.init() + + @classmethod + def tearDownClass(cls): + rclpy.shutdown() + + def setUp(self): + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ROSScenarioExecution() + self.scenario_dir = get_package_share_directory('scenario_execution') + + self.node = rclpy.create_node('test_node') + self.node.declare_parameter('testBoolParam', False) + self.node.declare_parameter('testFloatParam', 0.) + self.bool_value = self.node.get_parameter('testBoolParam').value + self.float_value = self.node.get_parameter('testFloatParam').value + self.node.add_on_set_parameters_callback(self.callback) + # self.srv = self.node.create_service(SetBool, "/bla", self.service_callback) + + self.executor = rclpy.executors.MultiThreadedExecutor() + self.executor.add_node(self.node) + self.executor_thread = threading.Thread(target=self.executor.spin, daemon=True) + self.executor_thread.start() + + def tearDown(self): + self.node.destroy_node() + + def callback(self, params): + print(f"set_parameters callback called.") + for param in params: + print(f" {param.name}: {param.value}") + if param.name == "testBoolParam": + self.bool_value = param.value + if param.name == "testFloatParam": + self.float_value = param.value + return SetParametersResult(successful=True) + + def test_success(self): + scenarios = self.parser.process_file(os.path.join( + self.scenario_dir, 'scenarios', 'test', 'test_ros_set_node_parameter.osc'), False) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) + self.assertTrue(self.bool_value) + self.assertEqual(self.float_value, 3.14) diff --git a/scenario_execution/test/test_ros_topic_publish.py b/scenario_execution/test/test_ros_topic_publish.py new file mode 100644 index 00000000..b72cffe2 --- /dev/null +++ b/scenario_execution/test/test_ros_topic_publish.py @@ -0,0 +1,69 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import unittest +import threading + +from ament_index_python.packages import get_package_share_directory + +import rclpy +from std_msgs.msg import Bool + +from scenario_execution import ROSScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger + + +class TestRosTopicPublish(unittest.TestCase): + # pylint: disable=missing-function-docstring,missing-class-docstring + @classmethod + def setUpClass(cls): + rclpy.init() + + @classmethod + def tearDownClass(cls): + rclpy.shutdown() + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ROSScenarioExecution() + + self.scenario_dir = get_package_share_directory('scenario_execution') + + self.received_msgs = [] + self.node = rclpy.create_node('test_node') + self.srv = self.node.create_subscription(Bool, "/bla", self.callback, 10) + + self.executor = rclpy.executors.MultiThreadedExecutor() + self.executor.add_node(self.node) + self.executor_thread = threading.Thread(target=self.executor.spin, daemon=True) + self.executor_thread.start() + + def tearDown(self): + self.node.destroy_node() + + def callback(self, msg): + self.received_msgs.append(msg) + + def test_success(self): + scenarios = self.parser.process_file(os.path.join( + self.scenario_dir, 'scenarios', 'test', 'test_ros_topic_publish.osc'), False) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) + self.assertEqual(len(self.received_msgs), 1) diff --git a/scenario_execution_base/MANIFEST.in b/scenario_execution_base/MANIFEST.in new file mode 100644 index 00000000..f597b803 --- /dev/null +++ b/scenario_execution_base/MANIFEST.in @@ -0,0 +1 @@ +include scenario_execution_base/lib_osc/*.osc \ No newline at end of file diff --git a/scenario_execution_base/README.md b/scenario_execution_base/README.md new file mode 100644 index 00000000..83da6452 --- /dev/null +++ b/scenario_execution_base/README.md @@ -0,0 +1,11 @@ +# Scenario Execution Base Package + +The `scenario_execution_base` package is the base package for scenario execution. It provides functionalities like parsing, py-trees creation and execution. + +It provides the following scenario execution libraries: + +- `standard.osc`: The OpenSCENARIO 2 standard library. It is slightly modified to be in sync with the feature set of scenario execution. For convenience, numerical struct members are initialized with 0. +- `robotics.osc`: robotic-specific specifications +- `helper.osc`: helper actions +- `networking.osc`: actions related to networking + diff --git a/scenario_execution_base/package.xml b/scenario_execution_base/package.xml new file mode 100644 index 00000000..d6f65b08 --- /dev/null +++ b/scenario_execution_base/package.xml @@ -0,0 +1,21 @@ + + + + scenario_execution_base + 1.0.0 + Robotics Scenario Execution + Intel Labs + Intel Labs + Apache-2.0 + + py_trees + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/scenario_execution_base/resource/scenario_execution_base b/scenario_execution_base/resource/scenario_execution_base new file mode 100644 index 00000000..e69de29b diff --git a/scenario_execution_base/scenario_execution_base/__init__.py b/scenario_execution_base/scenario_execution_base/__init__.py new file mode 100644 index 00000000..bbdeabb5 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/__init__.py @@ -0,0 +1,30 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from . import actions +from . import utils +from . import model +from scenario_execution_base.scenario_execution import ScenarioExecution +from scenario_execution_base.utils.logging import BaseLogger, Logger + +__all__ = [ + 'actions', + 'utils', + 'model', + 'BaseLogger', + "Logger", + 'ScenarioExecution' +] diff --git a/scenario_execution_base/scenario_execution_base/actions/__init__.py b/scenario_execution_base/scenario_execution_base/actions/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/actions/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_execution_base/scenario_execution_base/actions/log.py b/scenario_execution_base/scenario_execution_base/actions/log.py new file mode 100644 index 00000000..1dc82761 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/actions/log.py @@ -0,0 +1,54 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import py_trees +from py_trees.common import Status + + +class Log(py_trees.behaviour.Behaviour): + """ + Class for logging + + Args: + msg [str]: message to log + """ + + def __init__(self, name, msg: str): + super().__init__(name) + self.msg = msg + self.published = False + self.logger = None + + def setup(self, **kwargs) -> None: + """ + setup before ticking + + Args: + kwargs: arguments passed from py_trees.behaviour.Behaviour + """ + self.logger = kwargs['logger'] + + def update(self) -> py_trees.common.Status: + """ + execute the action + + return: + py_trees.common.Status.SUCCESS if the action is executed + """ + if not self.published: + self.published = True + self.logger.info(self.msg) + return Status.SUCCESS diff --git a/scenario_execution_base/scenario_execution_base/actions/run_external_process.py b/scenario_execution_base/scenario_execution_base/actions/run_external_process.py new file mode 100644 index 00000000..fcac0673 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/actions/run_external_process.py @@ -0,0 +1,135 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import py_trees # pylint: disable=import-error +import subprocess # nosec B404 +from threading import Thread +from collections import deque + + +class RunExternalProcess(py_trees.behaviour.Behaviour): + """ + Class to execute an external process. It finishes when the process finishes + + Args: + command[str]: external command to execute + """ + + def __init__(self, name, command=None): + super().__init__(name) + self.command = command + self.executed = False + self.process = None + self.log_stdout_thread = None + self.log_stderr_thread = None + self.output = deque() + + def update(self) -> py_trees.common.Status: + """ + Start/monitor external process + + return: + py_trees.common.Status + """ + if not self.executed: + self.executed = True + try: + self.process = subprocess.Popen( + self.command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except Exception as e: # pylint: disable=broad-except + self.logger.error(str(e)) + return py_trees.common.Status.FAILURE + + self.feedback_message = f"Executing '{self.command}'" # pylint: disable= attribute-defined-outside-init + self.on_executed() + + def log_output(out, log_fct, buffer): + try: + for line in iter(out.readline, b''): + msg = line.decode().strip() + if log_fct: + log_fct(msg) + buffer.append(msg) + out.close() + except ValueError: + pass + + self.log_stdout_thread = Thread(target=log_output, args=( + self.process.stdout, self.get_logger_stdout(), self.output)) + self.log_stdout_thread.daemon = True # die with the program + self.log_stdout_thread.start() + + self.log_stderr_thread = Thread(target=log_output, args=( + self.process.stderr, self.get_logger_stderr(), self.output)) + self.log_stderr_thread.daemon = True # die with the program + self.log_stderr_thread.start() + + if self.process is None: + return py_trees.common.Status.FAILURE + + ret = self.process.poll() + + if ret is None: + return self.check_running_process() + else: + return self.on_process_finished(ret) + + def get_logger_stdout(self): + """ + get logger for stderr messages + """ + return self.logger.info + + def get_logger_stderr(self): + """ + get logger for stderr messages + """ + return self.logger.error + + def check_running_process(self): + """ + hook to check running process + + return: + py_trees.common.Status + """ + return py_trees.common.Status.RUNNING + + def on_process_finished(self, ret): + """ + hook to check finished process + + return: + py_trees.common.Status + """ + if ret == 0: + self.feedback_message = f"Successfully executed '{self.command}'" # pylint: disable= attribute-defined-outside-init + return py_trees.common.Status.SUCCESS + else: + self.feedback_message = f"Execution of '{self.command}' failed with {ret}" # pylint: disable= attribute-defined-outside-init + return py_trees.common.Status.FAILURE + + def on_executed(self): + """ + hook for subclassed + """ + pass + + def set_command(self, command): + self.command = command + + def get_command(self): + return self.command diff --git a/scenario_execution_base/scenario_execution_base/get_osc_library.py b/scenario_execution_base/scenario_execution_base/get_osc_library.py new file mode 100644 index 00000000..17179c3c --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/get_osc_library.py @@ -0,0 +1,26 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +def get_robotics_library(): + return 'scenario_execution_base', 'robotics.osc' + + +def get_helpers_library(): + return 'scenario_execution_base', 'helpers.osc' + + +def get_standard_library(): + return 'scenario_execution_base', 'standard.osc' diff --git a/scenario_execution_base/scenario_execution_base/lib_osc/helpers.osc b/scenario_execution_base/scenario_execution_base/lib_osc/helpers.osc new file mode 100644 index 00000000..c57fbae5 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/lib_osc/helpers.osc @@ -0,0 +1,8 @@ + +action log: + # Print out a message + msg: string # Message to print + +action run_external_process: + # Run an external process + command: string # Command to execute diff --git a/scenario_execution_base/scenario_execution_base/lib_osc/robotics.osc b/scenario_execution_base/scenario_execution_base/lib_osc/robotics.osc new file mode 100644 index 00000000..b55b38e1 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/lib_osc/robotics.osc @@ -0,0 +1,4 @@ +import osc.standard + +actor differential_drive_robot inherits osc_actor: + namespace: string = '' diff --git a/scenario_execution_base/scenario_execution_base/lib_osc/standard.osc b/scenario_execution_base/scenario_execution_base/lib_osc/standard.osc new file mode 100644 index 00000000..52d1cb94 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/lib_osc/standard.osc @@ -0,0 +1,1227 @@ +########### +# Standard: OpenSCENARIO 2.0 domain model +# Copyright: ASAM 2021-2022 +# Version: +# Standard Publication Date: +# Publication Reference Document: +# Reference Document Date: +########### + +# ATTENTION: This file is modified to be compatible with +# the feature set of scenario execution. + +######################## +# Scalar types and units +######################## + +# tag::library-physical-length[] +type length is SI(m: 1) +unit nanometer of length is SI(m: 1, factor: 0.000000001) +unit nm of length is SI(m: 1, factor: 0.000000001) +unit micrometer of length is SI(m: 1, factor: 0.000001) +unit millimeter of length is SI(m: 1, factor: 0.001) +unit mm of length is SI(m: 1, factor: 0.001) +unit centimeter of length is SI(m: 1, factor: 0.01) +unit cm of length is SI(m: 1, factor: 0.01) +unit meter of length is SI(m: 1, factor: 1) +unit m of length is SI(m: 1, factor: 1) +unit kilometer of length is SI(m: 1, factor: 1000) +unit km of length is SI(m: 1, factor: 1000) +unit inch of length is SI(m: 1, factor: 0.0254) +unit feet of length is SI(m: 1, factor: 0.3048) +unit mile of length is SI(m: 1, factor: 1609.344) +unit mi of length is SI(m: 1, factor: 1609.344) +# end::library-physical-length[] + +# tag::library-physical-time[] +type time is SI(s: 1) +unit millisecond of time is SI(s: 1, factor: 0.001) +unit ms of time is SI(s: 1, factor: 0.001) +unit second of time is SI(s: 1, factor: 1) +unit sec of time is SI(s: 1, factor: 1) +unit s of time is SI(s: 1, factor: 1) +unit minute of time is SI(s: 1, factor: 60) +unit min of time is SI(s: 1, factor: 60) +unit hour of time is SI(s: 1, factor: 3600) +unit h of time is SI(s: 1, factor: 3600) +# end::library-physical-time[] + +# tag::library-physical-speed[] +type speed is SI(m: 1, s: -1) +unit meter_per_second of speed is SI(m: 1, s: -1, factor: 1) +unit mps of speed is SI(m: 1, s: -1, factor: 1) +unit kilometer_per_hour of speed is SI(m: 1, s: -1, factor: 0.277777778) +unit kmph of speed is SI(m: 1, s: -1, factor: 0.277777778) +unit kph of speed is SI(m: 1, s: -1, factor: 0.277777778) +unit mile_per_hour of speed is SI(m: 1, s: -1, factor: 0.447038889) +unit mph of speed is SI(m: 1, s: -1, factor: 0.447038889) +unit miph of speed is SI(m: 1, s: -1, factor: 0.447038889) +unit mmph of speed is SI(m: 1, s: -1, factor: 0.000000278) +unit millimeter_per_hour of speed is SI(m: 1, s: -1, factor: 0.000000278) +# end::library-physical-speed[] + +# tag::library-physical-acceleration[] +type acceleration is SI(m: 1, s: -2) +unit meter_per_sec_sqr of acceleration is SI(m: 1, s: -2, factor: 1) +unit mpsps of acceleration is SI(m: 1, s: -2, factor: 1) +unit mpss of acceleration is SI(m: 1, s: -2, factor: 1) +unit kilometer_per_hour_per_sec of acceleration is SI(m: 1, s: -2, factor: 0.277777778) +unit kmphps of acceleration is SI(m: 1, s: -2, factor: 0.277777778) +unit mile_per_hour_per_sec of acceleration is SI(m: 1, s: -2, factor: 0.447038889) +unit miphps of acceleration is SI(m: 1, s: -2, factor: 0.447038889) +# end::library-physical-acceleration[] + +# tag::library-physical-jerk[] +type jerk is SI(m: 1, s: -3) +unit meter_per_sec_cubed of jerk is SI(m: 1, s: -3, factor: 1) +unit mpspsps of jerk is SI(m: 1, s: -3, factor: 1) +unit mile_per_sec_cubed of jerk is SI(m: 1, s: -3, factor: 1609.344) +unit mipspsps of jerk is SI(m: 1, s: -3, factor: 1609.344) +# end::library-physical-jerk[] + +# tag::library-physical-angle[] +type angle is SI(rad: 1) +unit degree of angle is SI(rad: 1, factor: 57.295779513) +unit deg of angle is SI(rad: 1, factor: 57.295779513) +unit radian of angle is SI(rad: 1, factor: 1) +unit rad of angle is SI(rad: 1, factor: 1) +# end::library-physical-angle[] + +# tag::library-physical-angular_rate[] +type angular_rate is SI(rad: 1, s: -1) +unit degree_per_sec of angular_rate is SI(rad: 1, s: -1, factor: 57.295779513) +unit degps of angular_rate is SI(rad: 1, s: -1, factor: 57.295779513) +unit radian_per_sec of angular_rate is SI(rad: 1, s: -1, factor: 1) +unit radps of angular_rate is SI(rad: 1, s: -1, factor: 1) +# end::library-physical-angular_rate[] + +# tag::library-physical-angular_acceleration[] +type angular_acceleration is SI(rad: 1, s: -2) +unit degree_per_sec_sqr of angular_acceleration is SI(rad: 1, s: -2, factor: 57.295779513) +unit degpsps of angular_acceleration is SI(rad: 1, s: -2, factor: 57.295779513) +unit radian_per_sec_sqr of angular_acceleration is SI(rad: 1, s: -2, factor: 1) +unit radpsps of angular_acceleration is SI(rad: 1, s: -2, factor: 1) +# end::library-physical-angular_acceleration[] + +# tag::library-physical-mass[] +type mass is SI(kg: 1) +unit gram of mass is SI(kg: 1, factor: 0.001) +unit kilogram of mass is SI(kg: 1, factor: 1) +unit kg of mass is SI(kg: 1, factor: 1) +unit ton of mass is SI(kg: 1, factor: 1000) +unit pound of mass is SI(kg: 1, factor: 0.45359237) +unit lb of mass is SI(kg: 1, factor: 0.45359237) +# end::library-physical-mass[] + +# tag::library-physical-temperature[] +type temperature is SI(K: 1) +unit K of temperature is SI(K: 1, factor: 1) +unit kelvin of temperature is SI(K: 1, factor: 1) +unit celsius of temperature is SI(K: 1, factor: 1, offset: 273.15) +unit C of temperature is SI(K: 1, factor: 1, offset: 273.15) +unit fahrenheit of temperature is SI(K: 1, factor: 0.555555556, offset: 255.372222222) +unit F of temperature is SI(K: 1, factor: 0.555555556, offset: 255.372222222) +# end::library-physical-temperature[] + +# tag::library-physical-pressure[] +type pressure is SI(kg: 1, m: -1, s: -2) +unit newton_per_meter_sqr of pressure is SI(kg: 1, m: -1, s: -2, factor: 1) +unit Pa of pressure is SI(kg: 1, m: -1, s: -2, factor: 1) +unit pascal of pressure is SI(kg: 1, m: -1, s: -2, factor: 1) +unit hPa of pressure is SI(kg: 1, m: -1, s: -2, factor: 100) +unit atm of pressure is SI(kg: 1, m: -1, s: -2, factor: 101325) +# end::library-physical-pressure[] + +# tag::library-physical-luminous_intensity[] +type luminous_intensity is SI(cd: 1) +unit cd of luminous_intensity is SI(cd: 1, factor: 1) +unit candela of luminous_intensity is SI(cd: 1, factor: 1) +# end::library-physical-luminous_intensity[] + +# tag::library-physical-luminous_flux[] +type luminous_flux is SI(cd: 1, rad: 2) +unit lm of luminous_flux is SI(cd: 1, rad: 2, factor: 1) +unit lumen of luminous_flux is SI(cd: 1, rad: 2, factor: 1) +# end::library-physical-luminous_flux[] + +# tag::library-physical-illuminance[] +type illuminance is SI(cd: 1, rad: 2, m: -2) +unit lx of illuminance is SI(cd: 1, rad: 2, m: -2, factor: 1) +unit lux of illuminance is SI(cd: 1, rad: 2, m: -2, factor: 1) +# end::library-physical-illuminance[] + +# tag::library-physical-electrical_current[] +type electrical_current is SI(A: 1) +unit ampere of electrical_current is SI(A: 1, factor: 1) +unit A of electrical_current is SI(A: 1, factor: 1) +# end::library-physical-electrical_current[] + +# tag::library-physical-amount_of_substance[] +type amount_of_substance is SI(mol: 1) +unit mole of amount_of_substance is SI(mol: 1, factor: 1) +unit mol of amount_of_substance is SI(mol: 1, factor: 1) +# end::library-physical-amount_of_substance[] + +########### +# Structs +########### + +# tag::library-position_3d[] +struct position_3d: + x: length = 0.0m + y: length = 0.0m + z: length = 0.0m + #def norm() -> length is undefined +# end::library-position_3d[] + +# tag::library-celestial_position_2d[] +struct celestial_position_2d: + azimuth: angle = 0.0rad + elevation: angle = 0.0rad +# end::library-celestial_position_2d[] + +# tag::library-geodetic_position_2d[] +struct geodetic_position_2d: + latitude: angle = 0.0rad + longitude: angle = 0.0rad +# end::library-geodetic_position_2d[] + +# tag::library-orientation_3d[] +struct orientation_3d: + roll: angle = 0.0rad + pitch: angle = 0.0rad + yaw: angle = 0.0rad +# end::library-orientation_3d[] + +# tag::library-pose_3d[] +struct pose_3d: + position: position_3d + orientation: orientation_3d +# end::library-pose_3d[] + +# tag::library-translational_velocity_3d[] +struct translational_velocity_3d: + x: speed = 0.0mps + y: speed = 0.0mps + z: speed = 0.0mps +# def norm() -> speed is undefined +# end::library-translational_velocity_3d[] + +# tag::library-orientation_rate_3d[] +struct orientation_rate_3d: + roll: angular_rate = 0.0radps + pitch: angular_rate = 0.0radps + yaw: angular_rate = 0.0radps +# end::library-orientation_rate_3d[] + +# tag::library-velocity_6d[] +struct velocity_6d: + translational: translational_velocity_3d + angular: orientation_rate_3d +# end::library-velocity_6d[] + +# tag::library-translational_acceleration_3d[] +struct translational_acceleration_3d: + x: acceleration = 0.0mpsps + y: acceleration = 0.0mpsps + z: acceleration = 0.0mpsps +# def norm() -> acceleration is undefined +# end::library-translational_acceleration_3d[] + +# tag::library-orientation_acceleration_3d[] +struct orientation_acceleration_3d: + roll: angular_acceleration = 0.0radpsps + pitch: angular_acceleration = 0.0radpsps + yaw: angular_acceleration = 0.0radpsps +# end::library-orientation_acceleration_3d[] + +# tag::library-acceleration_6d[] +struct acceleration_6d: + translational: translational_acceleration_3d + angular: orientation_acceleration_3d +# end::library-acceleration_6d[] + +struct axle: + max_steering: angle # Mandatory: Maximum steering angle for the wheels on the axle. + wheel_diameter: length # Mandatory: Diameter for the wheels on this axle + track_width: length # Mandatory: Distance between the centerline of the outer wheels on opposing sides of the axle + position_x: length # Mandatory: Longitudinal position of the axle in the x-axis of the vehicle. For a 2-axle vehicle, the rear axle must have position_x = 0m + position_z: length # Mandatory: Vertical position of the axle in the vehicle's z-axis + number_of_wheels: uint # Mandatory: Number of wheels on the axle. + +struct bounding_box: + center: position_3d # Mandatory: Represents the geometrical center of the bounding box expressed in coordinates that refer to the coordinate system of the physical_object + length: length # Mandatory: Dimension in x-direction of the coordinate system of the physical_object + width: length # Mandatory: Dimension in y-direction of the coordinate system of the physical_object + height: length # Mandatory: Dimension in z-direction of the coordinate system of the physical_object + +struct crossing_type: + marking: crossing_marking # Optional: Define the type of markings on the crossing + use: crossing_use # Optional: Define the type of use for the crossing + elevation: crossing_elevation # Optional: Define the type of elevation for the crossing + + +######################### +# MODIFICATION: type definition must be done before usage +######################### + +enum path_interpolation: [ + straight_line, # Join the points with straight lines + smooth] # Join the points with a smooth line + +struct route: # Object of map + length: length # Optional: Nominal length of the route, measured along the s-axis of the route. Does not apply to route_point + directionality: directionality # Mandatory: Directionality for movement of traffic_participant actors on the route + min_lanes: uint # Optional: Minimum number of drivable lanes along this route. Applies only to these children: road, lane_section + max_lanes: uint # Optional: Maximum number of drivable lanes along this route. Applies only to these children: road, lane_section + anchors: list of string # Optional: The strings in here can be matched to unique items in the map files specified in file_name +# def start_point() -> route_point is undefined +# def end_point() -> route_point is undefined + +struct route_point inherits route_element: + route: route # Mandatory: route in which this point is located + #s: length = 0.0m # Optional: Coordinate along the s-axis of the corresponding route #TODO: s gets misinterpreted + t: length = 0.0m # Optional: Coordinate along the t-axis of the corresponding route + +struct xyz_point inherits route_element: + position: position_3d # Optional: Position in Cartesian (xyz) coordinates + +struct odr_point inherits route_element: + road_id: string # Mandatory: ASAM OpenDRIVE identifier for the road + lane_id: string # Optional: ASAM OpenDRIVE identifier for the lane. If specified, the t-coordinate is measured from the lane centerline. If not specified, the t-coordinate is measured from the ASAM OpenDRIVE reference line +# s: length = 0.0m # Optional: Coordinate along the ASAM OpenDRIVE s-axis + t: length = 0.0m # Optional: Coordinate along the ASAM OpenDRIVE t-axis + +# tag::library-path[] +struct path inherits route: + points: list of pose_3d # Mandatory: List of points in world x-y-z-coordinates. The individual pose_3d elements can have unconstrained coordinates. + interpolation: path_interpolation # Mandatory: Choose how to join the points of the path. + +struct relative_path: + interpolation: path_interpolation # Mandatory: Choose how to join the points of the path. + +struct relative_path_pose_3d inherits relative_path: + points: list of pose_3d # Mandatory: List of points in world x-y-z-coordinates. The individual pose_3d elements can have unconstrained coordinates. + +struct relative_path_st inherits relative_path: + points: list of route_point # Mandatory: Sequence of route_point that form the relative path + +struct relative_path_odr inherits relative_path: + points: list of odr_point # Mandatory: Sequence of odr_point that form the relative path +# end::library-path[] + +# tag::library-trajectory[] +struct trajectory: + points: list of pose_3d # Mandatory: List of points in world x-y-z-coordinates. The individual pose_3d elements can have unconstrained coordinates. + time_stamps: list of time # Mandatory: Time stamps for each element in points. The lists time_stamps and points must have the same length + interpolation: path_interpolation # Mandatory: Choose how to join the points of the trajectory. + +struct relative_trajectory: + time_stamps: list of time # Mandatory: Time stamps for each element in points. The lists time_stamps and points must have the same length + interpolation: path_interpolation # Mandatory: Choose how to join the points of the trajectory. + +struct relative_trajectory_pose_3d inherits relative_trajectory: + points: list of pose_3d # Mandatory: List of points in world x-y-z-coordinates. The individual pose_3d elements can have unconstrained coordinates. + +struct relative_trajectory_st inherits relative_trajectory: + points: list of route_point # Mandatory: Sequence of route_point that form the relative trajectory + +struct relative_trajectory_odr inherits relative_trajectory: + points: list of odr_point # Mandatory: Sequence of odr_point that form the relative trajectory +# end::library-trajectory[] + +# tag::library-shape-any[] +struct any_shape +# def duration() -> time is undefined + +struct any_acceleration_shape inherits any_shape +# def compute(time: time) -> acceleration is undefined + +struct any_speed_shape inherits any_shape +# def compute(time: time) -> speed is undefined + +struct any_position_shape inherits any_shape +# def compute(time: time) -> length is undefined + +struct any_lateral_shape inherits any_shape +# def compute(time: time) -> length is undefined +# end::library-shape-any[] + +# tag::library-shape-common[] +struct common_acceleration_shape inherits any_acceleration_shape: + rate_profile: dynamic_profile + rate_peak: jerk = 0.0mpspsps + target: acceleration = 0.0mpsps + +struct common_speed_shape inherits any_speed_shape: + rate_profile: dynamic_profile + rate_peak: acceleration = 0.0mpsps + target: speed = 0.0mps + +struct common_position_shape inherits any_position_shape: + rate_profile: dynamic_profile + rate_peak: speed = 0.0mps + target: length = 0.0m + +struct common_lateral_shape inherits any_lateral_shape: + rate_profile: dynamic_profile + rate_peak: speed = 0.0mps + target: length = 0.0m +# end::library-shape-common[] + +struct bm_engine # Reference to an object representing the bm_engine, whose content is implementation-dependent. + # Fields to be defined by the user or implementation + +struct behavioral_model: + bm_engine: bm_engine # Reference to the "behavioral model engine" + +########### +# Enums +########### + +enum color: [ + white, # RGB(255,255,255) + silver, # RGB(192,192,192) + gray, # RGB(128,128,128) + black, # RGB(0,0,0) + red, # RGB(255,0,0) + maroon, # RGB(128,0,0) + yellow, # RGB(255,255,0) + olive, # RGB(128,128,0) + lime, # RGB(0,255,0) + green, # RGB(0,128,0) + aqua, # RGB(0,255,255) + teal, # RGB(0,128,128) + blue, # RGB(0,0,255) + navy, # RGB(0,0,128) + fuchsia, # RGB(255,255,0) + purple] # RGB(255,0,255) + +enum intended_infrastructure: [ + driving, # OpenDRIVE: "normal" drivable road, which is not one of the other types + sidewalk, # OpenDRIVE: Lane on which pedestrians can walk safely + biking, # OpenDRIVE: Lane reserved for Cyclists + rail, # OpenDRIVE: Lane reserved for trains + tram, # OpenDRIVE: Lane reserved for trams + bus, # OpenDRIVE: Lane reserved for bus + taxi, # OpenDRIVE: Lane reserved for taxi + hov] # OpenDRIVE: Lane reserved for High Occupancy Vehicles (HOV) + +enum vehicle_category: [ + car, # Power-driven vehicle with maximum mass not exceeding 3.5 t having at least four wheels comprising not more than eight seats in addition to the driver seat. (UN category L7,M1,N1) + bus, # Power-driven vehicle having at least four wheels comprising more than eight seats in addition to the driver seat.(UN category M2,M3) + truck, # Power-driven vehicle with maximum mass exceeding 3.5t having at least four wheels. Designed for the carriage of goods. (UN category N2,N3) + trailer, # Vehicle designed to be towed by another vehicle. Non-permanently connected to towing vehicle, meaning it can be separated by an operation without involving facilities normally only found in workshop. + vru_vehicle, # Vehicle without a crash-resistant passenger cell intended for one to few passengers or small goods transport. With its occupant it results in a vulnerable road user. (UN category L1-L6 plus bicycles, pedelecs, e-bicycles, personal mobility devices, wheelchairs, mobility scooters and so on.) + other] # Unspecified but known type of vehicle (for example, stroller, shopping cart, etc) + +enum driving_rule: [ + left_hand_traffic, # Traffic drives on the left side of the road + right_hand_traffic] # Traffic drives on the right side of the road + +enum directionality: [ + uni_direction, # A traffic_participant can move legally in only one direction along the s-axis. + longitudinal, # Usually applies to lane_type driving and vru_vehicles + bi_direction, # A traffic_participant can move legally in both directions along the longitudinal s-axis. Usually applies to lane_type driving and vru_vehicles + split, # Applies for multi-lane elements: there are lanes with opposing uni_direction traffic flow within the route + free, # A traffic_participant can legally move in any direction (longitudinal or lateral). Usually applies to lane_type pedestrian or lane_use mix_traffic_vru + none, # No expected traffic flow. Usually applies to lane_type non_driving + other] # Other type of directionality + +enum lane_type: [ + driving, # Driving lane for road vehicles. See the driving_lane_use subtype + non_driving, # Non-driving lanes in road vehicles infrastructure. See the non_driving_lane_use subtype + vru_vehicles, # Lanes designated for VRU vehicles. See the vru_vehicles_lane_use subtype + pedestrian, # Lanes for pedestrians. See the pedestrian_lane_use subtype + other] # If the lane has another type + +enum lane_use: [ + normal, # A normal driving lane for road vehicles (OSI). Should be used in combination with lane_type == driving. + exit, # A deceleration lane in parallel to the main road (OSI). Should be used in combination with lane_type == driving. + entry, # An acceleration lane in parallel to the main road (OSI). Should be used in combination with lane_type == driving. + on_ramp, # A ramp from rural or urban roads joining a motorway (OSI). Should be used in combination with lane_type == driving. + off_ramp, # A ramp leading off a motorway onto rural or urban roads (OSI). Should be used in combination with lane_type == driving. + connecting_ramp, # A ramp that connects two motorways (OSI). Should be used in combination with lane_type == driving. + hov, # A lane for High Occupancy Vehicles (HOV), usually in highways. Should be used in combination with lane_type == driving. + bus, # A lane restricted for use only by busses. Should be used in combination with lane_type == driving. + mixed_traffic_vru, # A lane for mixed car and vru (vehicle and pedestrian) traffic, normally in urban areas. Should be used in combination with lane_type == driving or vru_vehicles. + parking, # A lane with parking spaces (OSI). Should be used in combination with lane_type == non_driving. + stop, # A hard shoulder on motorways for emergency stops (OSI). Should be used in combination with lane_type == non_driving. + restricted, # A lane on which road vehicles should not drive (OSI). Should be used in combination with lane_type == non_driving. + border, # A hard border on the edge of a road (OSI). Should be used in combination with lane_type == non_driving. + shoulder, # A soft border on the edge of a road (OSI). Should be used in combination with lane_type == non_driving. + curb, # An elevated surface with different height compared to the drivable lanes. Should be used in combination with lane_type == non_driving. + median, # An inaccessible lane for road vehicles and pedestrians. Typically used to separate the traffic. Should be used in combination with lane_type == non_driving. + bicycle, # A lane that is designated for bicycles (OSI). Should be used in combination with lane_type == vru_vehicles. + motorcycle, # A lane that is designated for motorcycles. Should be used in combination with lane_type == vru_vehicles. + sidewalk, # A lane that is designated for pedestrians (OSI). Should be used in combination with lane_type == pedestrian. + protected_sidewalk, # A lane for pedestrians with a barrier to separate it from road traffic. Should be used in combination with lane_type == pedestrian. + none, # The lane has no use. + other] # The lane has another use. + +enum side_left_right: [ + left, + right] + +enum crossing_marking: [ # Has connection to crossing_type + unmarked, # No crossing-markings on the road + marked, # The road or walking surface has markings that indicate a crossing + zebra, # Common type of marked crossing with thick zebra stripes + other] # Other type of markings for the crossing + +enum crossing_use: [ # Has connection to crossing_type + pedestrian, # Crossing is used by pedestrians (person, animal) and/or vehicles that usually move on sidewalks (wheelchair, stroller) + animal, # Animal crossing. For example, on a rural road or highway + bicycle, # Crossing for bicycles + rail_road, # Crossing for rail vehicles (train, subway, tram, ...) + other] # Other use for crossing + +enum crossing_elevation: [ + road_level, # Crossing is at same level as driving surface + curb_level, # Crossing is elevated from driving surface, often at the same level as a walking surface (sidewalk) or curb + refuge_island, # Along the crossing, the elevation may change between road and curb levels. For example, with refuge island(s) in the middle + other] # Another elevation type + +enum junction_direction: [ + straight, # The out_road is 0deg relative to the in_road + right, # The out_road is 90deg relative to the in_road + u_turn, # The out_road is 180deg relative to the in_road + left, # The out_road is 270deg relative to the in_road + other] # If none of the above apply + +enum route_overlap_kind: [ + equal, # Both routes have the same length, and coincide at the start and end points + start, # Both routes coincide at their start points + end, # Both routes coincide at their end points + inside, # The first route is fully inside the second route. Their start and end points do not have to coincide + any, # Any part of the first route overlaps with any part of the second route + other] # If none of the above apply + +enum lateral_overlap_kind: [ + never, # The two routes never overlap laterally. They never share a common lane. + sometimes, # In some segments of the route, the two routes can share a common lane. + always ] # The always routes share a common lane. + +enum dynamic_profile: [ + none, # No specific dynamic profile + constant, # Use constant first derivative + smooth, # Use smooth first derivative + asap] # Reach value as soon as possible + +enum lane_change_side: [ + left, # Lane to the left of the reference entity + right, # Lane to the right of the reference entity + inside, # Lane to the inside of the reference entity + outside, # Lane to the outside of the reference entity + same] # Same lane as the reference entity + +enum gap_direction: [ + ahead, # Gap in the positive direction of the s-axis, with respect to the reference entity + behind, # Gap in the negative direction of the s-axis, with respect to the reference entity + left, # Gap in the positive direction of the t-axis, with respect to the reference entity + right, # Gap in the negative direction of the t-axis, with respect to the reference entity + inside, # Gap in the direction pointing towards opposing traffic + outside] # Gap in the direction pointing away from opposing traffic + +enum headway_direction: [ + ahead, # Headway in the positive direction of the s-axis, with respect to the reference entity + behind] # Headway in the negative direction of the s-axis, with respect to the reference entity + +enum lat_measure_by: [ + left_to_left, + left_to_center, + left_to_right, + center_to_left, + center_to_right, + right_to_left, + right_to_center, + right_to_right, + closest] + +enum yaw_measure_by: [ + length_to_length, + length_to_width, + width_to_length, + width_to_width, + relative_to_north, + relative_to_road] + +enum orientation_measured_by: [ + absolute, + relative_to_reference, + relative_to_road] + +enum movement_options: [ + prefer_physical, # Perform the movement physical if possible + prefer_non_physical, # Perform the non physical way if the implementation allows that. For example, a test track may ignore this request. + must_be_physical] # An error message is issued, if this action cannot be physically performed for any reason. + +enum connect_route_points: [ + road, # Use the road element that contains this point + lane_section, # Use the lane_section element that contains this point + lane, # Use the lane element that contains this point + crossing, # Use the crossing element that contains this point + waypoint] # Use the point itself. The route must pass exactly through this point + +enum at: [ + start, + end, + all] + +enum movement_mode: [ + monotonous, # This movement mode adheres to the laws of physics. On top of that it limits the level of surprise that a movement may have. + other] # Not necessarily monotonous. This is the default. + +enum track: [ + actual, # Actual or projected. The default is actual, meaning that the vehicle reacts to the behavior of the referenced vehicle. + projected] # + +enum distance_direction: [ + longitudinal, # Measure distance in the x-coordinate. Positive means that the `reference` is in front of the `physical_object` that calls the method. + lateral] # Measure distance in the y-coordinate. Positive means that the `reference` is to the left of the `physical_object` that calls the method. + +enum distance_mode: [ + reference_points, # Measures the distance between the reference points. + bounding_boxes] # Measures the distance between the bounding boxes. + +enum relative_transform: [ + world_relative, + object_relative, + road_relative, + lane_relative +] + +enum on_route_type: [ on_road, on_lane_section, on_lane, on_crossing ] + +enum route_distance_enum: [ from_start, from_end ] + +########### +# Actor +########### + +actor osc_actor + +actor physical_object inherits osc_actor: + bounding_box: bounding_box # Mandatory: See bounding_box struct + color: color # Optional: See color enum + geometry_reference: string # Optional: Opaque reference of an associated 3D geometry model of the physical object. It is implementation-specific how model references are resolved to 3D models. + center_of_gravity: position_3d # Mandatory: Center of gravity of the object. If unknown, the center of the bounding box may be used instead. + var pose: pose_3d # Position and orientation measured in world coordinates with world system as reference. +# def object_distance(reference: physical_object, direction: distance_direction, mode: distance_mode = reference_points) -> length is undefined +# def get_s_coord(route_type: on_route_type = on_road) -> length is undefined +# def get_t_coord(route_type: on_route_type= on_road) -> length is undefined +# def get_route_point(route_type: on_route_type = on_road) -> route_point is undefined + +actor stationary_object inherits physical_object + +actor movable_object inherits physical_object: +# def distance_along_route(route: route, from: route_distance_enum = from_start) -> length is undefined + var velocity: velocity_6d # Mandatory: Translational and rotational velocity measured in object coordinates with world system as reference. + var acceleration: acceleration_6d # Mandatory: Translational and rotational acceleration measured in object coordinates with world system as reference. + var speed: speed # Mandatory: Speed in center_of_gravity defined as sqrt(velocity.translational.x^2 + velocity.translational.y^2) * sign(velocity.translational.x) + +actor traffic_participant inherits movable_object: + intended_infrastructure: list of intended_infrastructure # Mandatory: See intended_infrastructure for definition. Intended usage is for further specification of an entity. For example, together with vehicle_category or to provide hints for implemenations where to spawn and / or auto-route entities. Note that multiple types of infrastructure can be assigned because of the list character of this type. +# def time_to_collision(reference: physical_object) -> time is undefined +# def time_headway(reference: physical_object) -> time is undefined +# def space_gap(reference: physical_object, direction: distance_direction) -> length is undefined + +actor vehicle inherits traffic_participant: + vehicle_category: vehicle_category # Mandatory: See vehicle_category + axles: list of axle # Mandatory: See axle + rear_overhang: length # Mandatory: Rear overhang of the vehicle or more explicitly the horizontal distance between the end of the bounding_box and the center of the rear axle. + +actor person inherits traffic_participant + +actor animal inherits traffic_participant + +########### +# environment (an Actor) +########### +struct weather: + air: air # Optional: See struct air + rain: precipitation # Optional: Liquid water in form of droplets falling under gravity. + snow: precipitation # Optional: Frozen water in delicately-crystalline flakes falling under gravity. + wind: wind # Optional: See struct wind + fog: fog # Optional: See struct fog + clouds: clouds # Optional: See struct clouds + +actor environment inherits osc_actor: + geodetic_position: geodetic_position_2d # Optional: Geodetic position of world coordinate frame regarding WGS84. Regarding usage for determination of angle of celestial light sources see remark above. + datetime: time # Optional: Date and time at start of scenario as Unix time, i.e. Number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap seconds. Regarding usage for determination of angle of celestial light sources see remark above. + var weather: weather # Optional: See struct weather + sun: celestial_light_source # Optional: Sun as instance of celestial_light_source. + moon: celestial_light_source # Optional: Moon as instance of celestial_light_source. +# def local_to_unix_time(year: uint, month: uint, day: uint, hour: uint, minute: uint, second: uint, time_zone: float) -> time is undefined + +struct air: + temperature: temperature = 25.0celsius # Optional: Temperature on ground level. + atmospheric_pressure: pressure = 101325pascal # Optional: Atmospheric pressure on ground level. + relative_humidity: float = 0.0 # Optional: Relative humidity on ground level. + +struct precipitation: + intensity: speed = 0.0mps # Optional: Global intensity of precipitation given as volumetric flux. In case of (partially) solid precipitation, the equivalent melted volume shall be considered. Note that volumetric flux is describing a volume flow across an area, but after reduction the unit results in the same unit as for speeds. As of now it is not possible in + +struct wind: + speed: speed # Mandatory: The expected value of wind speed. To estimate the expected value, rolling mean value over a specific short interval (for example, 3s) can be used. + direction: angle # Mandatory: The origin direction of the wind (not target direction) in the ground/x-y-plane with clockwise increasing values to match common definitions. This results in 0 deg for a wind blowing from the North 90 deg for a wind blowing from the East 90 deg, if x-axis and y-axis are mapped to East and North. + +struct fog: + visual_range: length # Mandatory: Value of optical range of visible light in the standard setting, which corresponds to a certain density of fog. + +struct clouds: + cloudiness: uint # Mandatory: Using okta scale to define which portion of the sky is covered with clouds. Ranging from 0 for completely clear sky to 8 for a completely overcast sky. Values above 8 shall not be used. + +struct celestial_light_source: + var position: celestial_position_2d # Mandatory: Position of the light source, see definition of physical type celestial_position_2d. + +########### +# Map (an Actor) +########### + +actor map inherits osc_actor: + map_file: string + routes: list of route + junctions: list of junction + driving_rule: driving_rule +# def odr_to_route_point(road_id: string, lane_id: string, s: length, t: length) -> route_point is undefined +# def xyz_to_route_point(x: length, y: length, z: length) -> route_point is undefined +# def route_point_to_xyz(route_point: route_point) -> xyz_point is undefined +# def outer_side() -> side_left_right is undefined +# def inner_side() -> side_left_right is undefined +# def create_route(routes: list of route, connect_points_by: connect_route_points, legal_route: bool) -> compound_route is undefined +# def create_route_point(route: route, s: length, t: length) -> route_point is undefined +# def create_xyz_point(x: length, y: length, z: length) -> xyz_point is undefined +# def create_odr_point(road_id: string, lane_id: string, s: length, t: length) -> odr_point is undefined +# def create_path(points: list of pose_3d, interpolation: path_interpolation) -> path is undefined +# def create_path_odr_points(points: list of odr_point, interpolation: path_interpolation, on_road_network: bool) -> path is undefined +# def create_path_route_points(points: list of route_point, interpolation: path_interpolation, on_road_network: bool) -> path is undefined +# def create_trajectory(points: list of pose_3d, time_stamps: list of time,interpolation: path_interpolation) -> trajectory is undefined +# def create_trajectory_odr_points(points: list of odr_point, time_stamps: list of time, interpolation: path_interpolation, on_road_network: bool) -> trajectory is undefined +# def create_trajectory_route_points(points: list of route_point, time_stamps: list of time, interpolation: path_interpolation, on_road_network: bool) -> trajectory is undefined +# def resolve_relative_path(relative_path: relative_path, reference: physical_object, transform: relative_transform) -> path is undefined +# def resolve_relative_trajectory(relative_trajectory: relative_trajectory, reference: physical_object, transform: relative_transform) -> trajectory is undefined +# def get_map_file() -> string is undefined #from map.map_file + +struct route: # Object of map + length: length # Optional: Nominal length of the route, measured along the s-axis of the route. Does not apply to route_point + directionality: directionality # Mandatory: Directionality for movement of traffic_participant actors on the route + min_lanes: uint # Optional: Minimum number of drivable lanes along this route. Applies only to these children: road, lane_section + max_lanes: uint # Optional: Maximum number of drivable lanes along this route. Applies only to these children: road, lane_section + anchors: list of string # Optional: The strings in here can be matched to unique items in the map files specified in file_name +# def start_point() -> route_point is undefined +# def end_point() -> route_point is undefined + +struct route_element inherits route # Interface Class (of route) + +struct road inherits route_element: + s_positive: list of lane_section # Mandatory: List of lane_section elements that flow in the positive direction of the road s-axis + s_negative: list of lane_section # Optional: List of lane_section elements that flow in the negative direction of the road s-axis + +struct lane_section inherits route_element: + road: road # Mandatory: Where the lane_section resides + lanes: list of lane # Mandatory: List of lanes that compose the lane_section + s_axis: lane # Mandatory: Choose, which lane is used to determine the s-axis of the lane_section. Must be a member of it.lanes + +struct lane inherits route_element: + lane_section: lane_section # Mandatory: Where the lane resides + lane_type: lane_type # Mandatory: Type of lane + lane_use: lane_use # Mandatory: A subtype of the lane_type. Use compatible pairs of lane_type and lane_use + width: length # Optional: Nominal width of the lane + +struct crossing inherits route_element: + start_lane: lane # Mandatory: Crossing starts on this lane + end_lane: lane # Mandatory: Crossing ends on this lane + start_s_coord: length # Mandatory: On the starts_from lane, the crossing connects at this point in the lane s-axis (and zero in the t-axis) + end_s_coord: length # Mandatory: On the ends_on lane, the crossing connects at this point in the lane s-axis (and zero in the t-axis) + width: length # Mandatory: Nominal width of the crossing, measured perpendicular to the crossing s-axis + crossing_type: crossing_type # Mandatory: Type of crossing + +struct junction: + roads: list of road # Mandatory: A list of road + +struct compound_route inherits route: + route_elements: list of route_element # Mandatory: A list of route_element. + +struct compound_lane inherits route: + lanes: list of lane # Mandatory: A list of lane + +########### +# Action - movable_object +########### + +action osc_actor.osc_action + +action movable_object.action_for_movable_object inherits osc_actor.osc_action + +action movable_object.move inherits movable_object.action_for_movable_object + +action movable_object.remain_stationary inherits movable_object.action_for_movable_object + +action movable_object.assign_position inherits movable_object.action_for_movable_object: + position: position_3d # Optional: Desired 3-dimensional position assigned by the user + route_point: route_point # Optional: Desired route_point assigned by the user + odr_point: odr_point # Optional: Desired odr_point assigned by the user + +action movable_object.assign_orientation inherits movable_object.action_for_movable_object: + target: orientation_3d # Mandatory: Desired 3-dimensional orientation assigned by the user + +action movable_object.assign_speed inherits movable_object.action_for_movable_object: + target: speed # Mandatory: Desired (scalar) speed assigned by the user + +action movable_object.assign_acceleration inherits movable_object.action_for_movable_object: + target: acceleration # Mandatory: Desired (scalar) acceleration assigned by the user + +action movable_object.replay_path inherits movable_object.action_for_movable_object: + absolute: path # Mandatory: Absolute path. Includes a list of points + relative: relative_path # Mandatory: Relative path. Includes a list of points +# reference: physical_object with: # Optional: Use with relative paths. Specify the reference entity that defines the origin for the point coordinates. Default: the actor itself +# keep(default it == actor) +# transform: relative_transform with: # Optional: Use with relative paths. Coordinates of the points are relative to the reference entity. Default = object_relative +# keep(default it == object_relative) +# start_offset: length with: # Optional: Offset at which to begin following the path, measured from the path's start. Default = 0m +# keep(default it == 0m) +# end_offset: length with: # Optional: Offset at which to end following the path, measured from the path's end. Default = 0m +# keep(default it == 0m) + +action movable_object.replay_trajectory inherits movable_object.action_for_movable_object: + absolute: trajectory # Mandatory: Absolute trajectory. Includes a list of points and a list of corresponding time stamps + relative: relative_trajectory # Mandatory: Relative trajectory. Includes a list of points and a list of corresponding time stamps +# reference: physical_object with: # Optional: Use with relative trajectories. Specify the reference entity that defines the origin for the point coordinates. Default: the actor itself +# keep(default it == actor) +# transform: relative_transform with: # Optional: Use with relative trajectories. Coordinates of the points are relative to the reference entity. Default = object_relative +# keep(default it == object_relative) +# start_offset: length with: # Optional: Offset at which to begin following the trajectory, measured from the trajectory's start. Default = 0m +# keep(default it == 0m) +# end_offset: length with: # Optional: Offset at which to end following the trajectory, measured from the trajectory's end. Default = 0m +# keep(default it == 0m) + +action movable_object.change_position inherits movable_object.action_for_movable_object: + target: position_3d # Mandatory: Target value for the position at the end of the action + interpolation: path_interpolation # Mandatory: The interpolation method used to join the start and end points + on_road_network: bool # Mandatory: The action takes place completely on the road network of the scenario + +action movable_object.change_speed inherits movable_object.action_for_movable_object: + target: speed # Mandatory: Target value for the speed at the end of the action +# rate_profile: dynamic_profile with: # Optional: Assign a shape for the change of the speed variable. This profile affects the acceleration during action execution +# keep(default it == none) + rate_peak: acceleration = 0.0mpsps # Optional: Target value for the peak acceleration that must be achieved during the action + +# action movable_object.keep_speed inherits movable_object.action_for_movable_object + +action movable_object.change_acceleration inherits movable_object.action_for_movable_object: + target: acceleration # Mandatory: Target value for the scalar acceleration at the end of the action +# rate_profile: dynamic_profile with: # Optional: Assign a shape for the change of the speed variable. This profile affects the jerk during action execution +# keep(default it == none) + rate_peak: jerk = 0.0mpspsps # Optional: Target value for the peak jerk that must be achieved during the action + +action movable_object.keep_acceleration inherits movable_object.action_for_movable_object + +action movable_object.follow_path inherits movable_object.action_for_movable_object: + absolute: path # Mandatory: Absolute path. Includes a list of points + relative: relative_path # Mandatory: Relative path. Includes a list of points +# reference: physical_object with: # Optional: Use with relative paths. Specify the reference entity that defines the origin for the point coordinates. Default: the actor itself +# keep(default it == actor) +# transform: relative_transform with: # Optional: Use with relative paths. Coordinates of the points are relative to the reference entity. Default = object_relative +# keep(default it == object_relative) +# start_offset: length with: # Optional: Offset at which to begin following the path, measured from the path's start. Default = 0m +# keep(default it == 0m) +# end_offset: length with: # Optional: Offset at which to end following the path, measured from the path's end. Default = 0m +# keep(default it == 0m) + +action movable_object.follow_trajectory inherits movable_object.action_for_movable_object: + absolute: trajectory # Mandatory: Absolute trajectory. Includes a list of points and a list of corresponding time stamps + relative: relative_trajectory # Mandatory: Relative trajectory. Includes a list of points and a list of corresponding time stamps +# reference: physical_object with: # Optional: Use with relative trajectories. Specify the reference entity that defines the origin for the point coordinates. Default: the actor itself +# keep(default it == actor) +# transform: relative_transform with: # Optional: Use with relative trajectories. Coordinates of the points are relative to the reference entity. Default = object_relative +# keep(default it == object_relative) +# start_offset: length with: # Optional: Offset at which to begin following the trajectory, measured from the trajectory's start. Default = 0m +# keep(default it == 0m) +# end_offset: length with: # Optional: Offset at which to end following the trajectory, measured from the trajectory's end. Default = 0m +# keep(default it == 0m) + +########### +# Action - vehicle +########### + +action vehicle.action_for_vehicle inherits movable_object.action_for_movable_object + +action vehicle.drive inherits vehicle.action_for_vehicle + +action vehicle.follow_lane inherits vehicle.action_for_vehicle: +# offset: length with: # Optional: Default=0.0. Offset from center of the lane for the actor to follow, using the t-axis of the lane +# keep(default it == 0.0m) +# rate_profile: dynamic_profile with: # Optional: Assign a shape for the change of the lateral position variable (t-axis). This profile affects the lateral velocity during action execution +# keep(default it == none) + rate_peak: speed = 0.0mps # Optional: Target value for the peak lateral velocity that must be achieved during the action + target: lane # Optional: The actor must be in this lane at the start, throughout, and the end of the action. If this argument is ignored, the actor follows the current lane when the action is invoked + +action vehicle.change_lane inherits vehicle.action_for_vehicle: +# num_of_lanes: int with: # Optional: The target lane is "num_of_lanes" to the side of the reference entity. Use in conjunction with "side" +# keep(default it == 1) + side: lane_change_side # Optional: Select on which side of the reference entity +# reference: physical_object with: # Optional: Default=it.actor. Reference to the entity that is used to determine the target lane. If this argument is omitted, the actor itself is used as reference +# keep(default it == actor) +# offset: length with: # Optional: Default=0.0. Target offset from center of the target lane that the actor follows at the end of the action +# keep(default it == 0.0m) +# rate_profile: dynamic_profile with: # Optional: Assign a shape for the change of the lateral position variable (t-axis). This profile affects the lateral velocity during action execution '/ +# keep(default it == none) + rate_peak: speed = 0.0mps # Optional: Target value for the peak lateral velocity that must be achieved during the action + target: lane # Mandatory: The actor starts and finishes the action in the target lane + +action vehicle.change_space_gap inherits vehicle.action_for_vehicle: + target: length # Mandatory: Target distance between the actor and the reference entity. Distance is measured according to the space_gap() method + direction: gap_direction # Mandatory: Placement of the actor with respect to the reference entity. [ahead, behind] means distance is measured in the s-axis. [left, right, inside, outside] means distance is measured in the t-axis + reference: physical_object # Mandatory: The actor reaches the driving distance to this reference entity + +action vehicle.keep_space_gap inherits vehicle.action_for_vehicle: + reference: physical_object # Mandatory: The actor keeps the driving distance to this reference entity + direction: distance_direction # Mandatory: Direction in which the space gap is kept with respect to the reference entity. [longitudinal] to keep distance in the s-axis. [lateral] to keep distance in the t-axis + +action vehicle.change_time_headway inherits vehicle.action_for_vehicle: + target: time # Mandatory: Target time headway between the actor and the reference entity. Time headway is measured according to the time_headway() method + direction: headway_direction # Mandatory: Placement of the actor with respect to the reference entity + reference: physical_object # Mandatory: The actor reaches the time headway to this reference entity + +action vehicle.keep_time_headway inherits vehicle.action_for_vehicle: + reference: physical_object # Mandatory: The actor keeps the driving distance to this reference entity + direction: headway_direction # Mandatory: Direction in which the space gap is kept with respect to the reference entity. [longitudinal] to keep distance in the s-axis. [lateral] to keep distance in the t-axis + +########### +# Action - person +########### + +action person.action_for_person inherits movable_object.action_for_movable_object + +action person.walk inherits person.action_for_person + +########### +# Action - environment +########### + +action environment.action_for_environment inherits osc_actor.osc_action + +action environment.air inherits environment.action_for_environment: + temperature: temperature = 25.0celsius + pressure: pressure = 101325pascal + relative_humidity: float = 0.0 + +action environment.rain inherits environment.action_for_environment: + intensity: speed = 0.0mps + +action environment.snow inherits environment.action_for_environment: + intensity: speed = 0.0mps + +action environment.wind inherits environment.action_for_environment: + speed: speed = 0.0mps + direction: angle = 0.0rad + +action environment.fog inherits environment.action_for_environment: + visual_range: length = 0.0m + +action environment.cloud inherits environment.action_for_environment: + cloudiness: uint = 0 + +action environment.assign_celestial_position inherits environment.action_for_environment: + light_source: celestial_light_source + azimuth: angle + elevation: angle + +##################################### +# Modifier - location based modifiers +##################################### + +# modifier position of movable_object.action_for_movable_object: +# distance: length # Mandatory: A value with a distance unit. +# time: time with: # Mandatory: A value with a time unit. +# keep(default it == 0sec) +# ahead_of, behind: physical_object # Optional: A named instance of the vehicle actor example: example, vehicle2 + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier distance of movable_object.action_for_movable_object: +# distance: length # Mandatory: The distance the actor should travel in the current movement. + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier lane of movable_object.action_for_movable_object: +# lane: uint with: # Mandatory: An integer indicating a required lane for a drive +# keep(default it == 1) +# same_as: physical_object +# side_of: physical_object +# side: side_left_right +# from: side_left_right + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier keep_lane of movable_object.action_for_movable_object: +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier lateral of movable_object.action_for_movable_object: +# distance: length with: # Mandatory: The offset from reference line. The default is [-10.0..10.0]centimeter. +# keep(default it in [-10.0cm..10.0cm]) +# left_of, right_of, same_as, side_of: physical_object # Optional: +# side: side_left_right # Optional: +# measure_by: lat_measure_by with: # Optional: This parameter specifies the measurement start and end points. +# keep(default it == closest) + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier yaw of movable_object.action_for_movable_object: +# angle: angle # Mandatory: A value with an angle unit. This parameter is mandatory +# relative_to: physical_object # Optional: A named instance of the vehicle actor example: example, vehicle2 +# measure_by: yaw_measure_by with: # Optional: Defines reference lines of the context and referenced objects, where width is an x-axis and long is a y-axis. +# keep(default it == relative_to_road) + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier orientation of movable_object.action_for_movable_object: +# yaw: angle # Mandatory: A value with an angle unit. This parameter is mandatory for either yaw, pitch and roll +# pitch: angle # Mandatory: A value with an angle unit. This parameter is mandatory for either yaw, pitch and roll +# roll: angle # Mandatory: A value with an angle unit. This parameter is mandatory for either yaw, pitch and roll +# relative_to: physical_object # Optional: A named instance of the vehicle actor example: example, vehicle2 +# measured_by: orientation_measured_by with: # Optional: defines how to measure the desired orientation. Values include absolute, relative_to_reference, relative_to_road. the default is relative_to_road +# keep(default it == relative_to_road) + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +#modifier along of movable_object.action_for_movable_object: +# route: route # Mandatory: The route to move on. +# start_offset: length with: # Optional: Offset at which to begin following the route, measured from the route's start. Default = 0m +# keep(default it == 0m) +# end_offset: length # Optional: Offset at which to end following the route, measured from the route's end. Default = 0m +# keep(default it == 0m) + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier keep_position of movable_object.action_for_movable_object: +# shape: any_shape # Optional: uses struct any_shape + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) + +#modifier along_trajectory of movable_object.action_for_movable_object: +# trajectory: trajectory # The trajectory to move along. +# start_offset: length with: # Offset at which to begin following the trajectory, measured from the trajectory's start. Default = 0m +# keep(default it == 0m) +# end_offset: length with: # Offset at which to end following the trajectory, measured from the trajectory's end. Default = 0m +# keep(default it == 0m) + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) + +# modifier stationary_object.location: +# pose: pose_3d # Mandatory. Location of the stationary object for the whole scenario. + +########################################### +# Modifier - rate of change based modifiers +########################################### + +# modifier speed of movable_object.action_for_movable_object: +# speed: speed # Mandatory: The vehicle's desired speed. +# faster_than, slower_than, same_as: physical_object # Optional: A named instance of the actor example: example, vehicle2 + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier acceleration of movable_object.action_for_movable_object: +# acceleration: acceleration # A value appended with an acceleration unit. +# faster_than, slower_than, same_as: physical_object # Optional: A named instance of the actor example: example, vehicle2 + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier keep_speed of movable_object.action_for_movable_object: +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier change_speed of movable_object.action_for_movable_object: +# speed: speed # Mandatory: The vehicle's desired speed. + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier physical_movement of movable_object.action_for_movable_object: +# option: movement_options + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier avoid_collisions of movable_object.action_for_movable_object: +# avoid: bool # Mandatory: Either true or false. + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# modifier change_lane of movable_object.action_for_movable_object: +# lane: uint with: +# keep(default it == 1) # Mandatory: The number of lanes to change from. The default is 1. +# side: side_left_right # Optional: Left or right. The side is randomized if not specified. + +# at: at with: # Optional: uses enum at to support: start | end | all (default: all) +# keep(default it == all) +# movement_mode: movement_mode with: # Optional: uses enum movement_mode: monotonous | other (default: other) +# keep(default it == other) +# track: track with: # Optional: uses enum track: actual | projected (default: actual) +# keep(default it == actual) +# shape: any_shape # Optional: uses struct any_shape + +# ################################ +# # Modifier - map based modifiers +# ################################ + +# modifier map.number_of_lanes: +# route: route # Mandatory: The route that has these constraints +# num_of_lanes: uint # Mandatory: The desired number of lanes +# lane_type: lane_type with: # Optional: Apply the constraint to the number of lanes with this type. +# keep(default it == driving) +# lane_use: lane_use with: # Optional: Apply the constraint to the number of lanes with this use. +# keep(default it == normal) +# directionality: directionality with: # Optional: Apply the constraint to the number of lanes with this directionality. +# keep(default it == uni_direction) + +# modifier map.routes_are_in_sequence: +# preceding: route # The first route +# succeeding: route # The second route, which follows after the first route. +# road: road # Optional: The road that will contain this sequence of routes. + +# modifier map.roads_follow_in_junction: +# junction: junction # The junction to be used. +# in_road: road # The chosen road that leads into the junction. +# out_road: road # The chosen road that leads away from the junction. +# direction: junction_direction # Indicates the direction of the out_road relative to the in_road. +# clockwise_count: uint # out_road is clockwise_count roads from in_road, counting clockwise. +# number_of_roads: uint # Total number of in_roads connected to the junction. +# in_lane: lane # The chosen lane within in_road. +# out_lane: lane # The chosen lane within out_road. +# junction_route: route # The element(s) that connect the in_lane or in_road to the out_lane or out_road within the junction. +# resulting_route: route # The route going from in_lane or in_road to the out_lane or out_road. + +# modifier map.routes_overlap: +# route1: route # Mandatory: The first of the overlapping routes. +# route2: route # Mandatory: The second of the overlapping routes. +# overlap_kind: route_overlap_kind # Mandatory: The type of expected overlap. Notice route1 is considered the first route to interpret the values of the enum. + +# modifier map.lane_side: +# lane1: lane # Mandatory: The first lane. +# side: side_left_right # Mandatory: Locate lane1 on this side of lane2. +# lane2: lane # Mandatory: The second lane. +# count: uint # Mandatory: How far is lane1 from lane2? +# lane_section: lane_section # Optional: lane_section where the lanes reside. + +# modifier map.compound_lane_side: +# lane1: compound_lane # Mandatory: The first compound_lane +# side: side_left_right # Mandatory: Locate lane1 on this side of lane2 +# lane2: compound_lane # Mandatory: The second compound_lane +# count: uint # Mandatory: number of from lane1 from lane2 +# route: route # Optional: The route where the compound lanes reside + +# modifier map.end_lane: +# lane: lane # Mandatory: This lane ends in its current lane_section + +# modifier map.start_lane: +# lane: lane # Mandatory: This lane starts in its current lane_section + +# modifier map.crossing_connects: +# crossing: crossing # Mandatory: The crossing that is connected to the specified lanes +# start_lane: lane # Mandatory: The lane where crossing starts (starting from the centerline of the lane) +# end_lane: lane # Mandatory: The destination lane where the crossing ends (ending on the centerline of the lane). +# start_s_coord: length # Mandatory: The crossing origin derived from a s-position along the centerline of start_lane. +# start_angle: angle with: # Optional: The angle at which the straight centerline of the crossing originates from the start lane. Default is perpendicular. +# keep(default it == 90deg) + +# modifier map.routes_are_opposite: +# route1: route # Mandatory: The first uni-directional route. +# route2: route # Mandatory: The second uni-directional route. +# containing_road: road # Mandatory: The road to which both routes belong. +# lateral_overlap: lateral_overlap_kind # Mandatory: Specifies if the routes overlap lateral, meaning they become a single two-way lane. + +# modifier map.set_map_file: +# file: string # Mandatory: The path and file name for the map file. diff --git a/scenario_execution_base/scenario_execution_base/model/__init__.py b/scenario_execution_base/scenario_execution_base/model/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_execution_base/scenario_execution_base/model/error.py b/scenario_execution_base/scenario_execution_base/model/error.py new file mode 100644 index 00000000..3de96a29 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/error.py @@ -0,0 +1,38 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +from antlr4 import ParserRuleContext + + +class OSC2ParsingError(Exception): + """ + Error class for OSC2 parser + """ + + def __init__(self, msg: str, context, *args) -> None: + super().__init__(*args) + self.msg = msg + if isinstance(context, ParserRuleContext): + self.line = context.start.line + self.column = context.start.column + self.context = context.getText() + self.filename = "" + else: + self.line = context[0] + self.column = context[1] + self.context = context[2] + self.filename = context[3] diff --git a/scenario_execution_base/scenario_execution_base/model/model_base_visitor.py b/scenario_execution_base/scenario_execution_base/model/model_base_visitor.py new file mode 100644 index 00000000..d82998fc --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/model_base_visitor.py @@ -0,0 +1,266 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +from .types import ModelElement, CompilationUnit, PhysicalTypeDeclaration, UnitDeclaration, SIBaseExponent, EnumDeclaration, EnumMemberDeclaration, EnumValueReference, InheritsCondition, StructDeclaration, StructInherits, ActionDeclaration, ActionInherits, ActorDeclaration, ActorInherits, FieldAccessExpression, FallExpression, FloatLiteral, FunctionApplicationExpression, Argument, BehaviorInvocation, BinaryExpression, BoolLiteral, CallDirective, CastExpression, CoverDeclaration, DoDirective, ElapsedExpression, ElementAccessExpression, DoMember, EmitDirective, EnumTypeExtension, EventCondition, EventDeclaration, EventFieldDecl, EventReference, EveryExpression, GlobalParameterDeclaration, Identifier, IdentifierReference, IntegerLiteral, KeepConstraintDeclaration, ListExpression, LogicalExpression, MethodBody, MethodDeclaration, ModifierDeclaration, ModifierInvocation, NamedArgument, OnDirective, ParameterDeclaration, PhysicalLiteral, ParameterReference, PositionalArgument, RangeExpression, RelationExpression, RecordDeclaration, RemoveDefaultDeclaration, RiseExpression, SampleExpression, ScenarioInherits, SIUnitSpecifier, StringLiteral, ScenarioDeclaration, StructuredTypeExtension, TernaryExpression, TypeTestExpression, UnaryExpression, Type, UntilDirective, VariableDeclaration, WaitDirective + + +class BaseVisitor(object): + def visit(self, tree): + return tree.accept(self) + + def visit_children(self, node): + result = self.default_result() + n = node.get_child_count() + for i in range(n): + if not self.should_visit_next_child(node, result): + return result + + c = node.get_child(i) + if isinstance(c, ModelElement): + child_result = c.accept(self) + result = self.aggregate_result(result, child_result) + + return result + + def default_result(self): + return [] + + def aggregate_result(self, aggregate, next_result): + if aggregate: + return [aggregate, next_result] + else: + return next_result + + def should_visit_next_child(self, node, current_result): + return True + + +class ModelBaseVisitor(BaseVisitor): # pylint: disable=too-many-public-methods + def visit_compilation_unit(self, node: CompilationUnit): + return self.visit_children(node) + + def visit_physical_type_declaration(self, node: PhysicalTypeDeclaration): + return self.visit_children(node) + + def visit_unit_declaration(self, node: UnitDeclaration): + return self.visit_children(node) + + def visit_si_base_exponent(self, node: SIBaseExponent): + return self.visit_children(node) + + def visit_enum_declaration(self, node: EnumDeclaration): + return self.visit_children(node) + + def visit_enum_member_declaration(self, node: EnumMemberDeclaration): + return self.visit_children(node) + + def visit_enum_value_reference(self, node: EnumValueReference): + return self.visit_children(node) + + def visit_inherits_condition(self, node: InheritsCondition): + return self.visit_children(node) + + def visit_struct_declaration(self, node: StructDeclaration): + return self.visit_children(node) + + def visit_struct_inherits(self, node: StructInherits): + return self.visit_children(node) + + def visit_actor_declaration(self, node: ActorDeclaration): + return self.visit_children(node) + + def visit_actor_inherits(self, node: ActorInherits): + return self.visit_children(node) + + def visit_scenario_declaration(self, node: ScenarioDeclaration): + return self.visit_children(node) + + def visit_scenario_inherits(self, node: ScenarioInherits): + return self.visit_children(node) + + def visit_action_declaration(self, node: ActionDeclaration): + return self.visit_children(node) + + def visit_action_inherits(self, node: ActionInherits): + return self.visit_children(node) + + def visit_modifier_declaration(self, node: ModifierDeclaration): + return self.visit_children(node) + + def visit_enum_type_extension(self, node: EnumTypeExtension): + return self.visit_children(node) + + def visit_structured_type_extension(self, node: StructuredTypeExtension): + return self.visit_children(node) + + def visit_global_parameter_declaration( + self, node: GlobalParameterDeclaration + ): + return self.visit_children(node) + + def visit_parameter_declaration(self, node: ParameterDeclaration): + return self.visit_children(node) + + def visit_parameter_reference(self, node: ParameterReference): + return self.visit_children(node) + + def visit_variable_declaration(self, node: VariableDeclaration): + return self.visit_children(node) + + def visit_event_declaration(self, node: EventDeclaration): + return self.visit_children(node) + + def visit_event_reference(self, node: EventReference): + return self.visit_children(node) + + def visit_event_field_declaration(self, node: EventFieldDecl): + return self.visit_children(node) + + def visit_event_condition(self, node: EventCondition): + return self.visit_children(node) + + def visit_method_declaration(self, node: MethodDeclaration): + return self.visit_children(node) + + def visit_method_body(self, node: MethodBody): + return self.visit_children(node) + + def visit_cover_declaration(self, node: CoverDeclaration): + return self.visit_children(node) + + def visit_record_declaration(self, node: RecordDeclaration): + return self.visit_children(node) + + def visit_argument(self, node: Argument): + return self.visit_children(node) + + def visit_named_argument(self, node: NamedArgument): + return self.visit_children(node) + + def visit_positional_argument(self, node: PositionalArgument): + return self.visit_children(node) + + def visit_keep_constraint_declaration(self, node: KeepConstraintDeclaration): + return self.visit_children(node) + + def visit_remove_default_declaration(self, node: RemoveDefaultDeclaration): + return self.visit_children(node) + + def visit_on_directive(self, node: OnDirective): + return self.visit_children(node) + + def visit_do_directive(self, node: DoDirective): + return self.visit_children(node) + + def visit_do_member(self, node: DoMember): + return self.visit_children(node) + + def visit_wait_directive(self, node: WaitDirective): + return self.visit_children(node) + + def visit_emit_directive(self, node: EmitDirective): + return self.visit_children(node) + + def visit_call_directive(self, node: CallDirective): + return self.visit_children(node) + + def visit_until_directive(self, node: UntilDirective): + return self.visit_children(node) + + def visit_behavior_invocation(self, node: BehaviorInvocation): + return self.visit_children(node) + + def visit_modifier_invocation(self, node: ModifierInvocation): + return self.visit_children(node) + + def visit_rise_expression(self, node: RiseExpression): + return self.visit_children(node) + + def visit_fall_expression(self, node: FallExpression): + return self.visit_children(node) + + def visit_elapsed_expression(self, node: ElapsedExpression): + return self.visit_children(node) + + def visit_every_expression(self, node: EveryExpression): + return self.visit_children(node) + + def visit_sample_expression(self, node: SampleExpression): + return self.visit_children(node) + + def visit_cast_expression(self, node: CastExpression): + return self.visit_children(node) + + def visit_type_test_expression(self, node: TypeTestExpression): + return self.visit_children(node) + + def visit_element_access_expression(self, node: ElementAccessExpression): + return self.visit_children(node) + + def visit_function_application_expression(self, node: FunctionApplicationExpression): + return self.visit_children(node) + + def visit_binary_expression(self, node: BinaryExpression): + return self.visit_children(node) + + def visit_unary_expression(self, node: UnaryExpression): + return self.visit_children(node) + + def visit_ternary_expression(self, node: TernaryExpression): + return self.visit_children(node) + + def visit_list_expression(self, node: ListExpression): + return self.visit_children(node) + + def visit_range_expression(self, node: RangeExpression): + return self.visit_children(node) + + def visit_physical_literal(self, node: PhysicalLiteral): + return self.visit_children(node) + + def visit_integer_literal(self, node: IntegerLiteral): + return self.visit_children(node) + + def visit_float_literal(self, node: FloatLiteral): + return self.visit_children(node) + + def visit_bool_literal(self, node: BoolLiteral): + return self.visit_children(node) + + def visit_string_literal(self, node: StringLiteral): + return self.visit_children(node) + + def visit_type(self, node: Type): + return self.visit_children(node) + + def visit_identifier(self, node: Identifier): + return self.visit_children(node) + + def visit_identifier_reference(self, node: IdentifierReference): + return self.visit_children(node) + + def visit_field_access_expression(self, node: FieldAccessExpression): + return self.visit_children(node) + + def visit_logical_expression(self, node: LogicalExpression): + return self.visit_children(node) + + def visit_si_unit_specifier(self, node: SIUnitSpecifier): + return self.visit_children(node) + + def visit_relation_expression(self, node: RelationExpression): + return self.visit_children(node) diff --git a/scenario_execution_base/scenario_execution_base/model/model_builder.py b/scenario_execution_base/scenario_execution_base/model/model_builder.py new file mode 100644 index 00000000..05ad277e --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/model_builder.py @@ -0,0 +1,1994 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +from .types import CompilationUnit, PhysicalTypeDeclaration, UnitDeclaration, EnumDeclaration, EnumMemberDeclaration, EnumValueReference, StructDeclaration, StructInherits, ActionDeclaration, ActionInherits, ActorDeclaration, ActorInherits, FieldAccessExpression, FloatLiteral, FunctionApplicationExpression, Argument, BehaviorInvocation, BinaryExpression, BoolLiteral, DoDirective, ElapsedExpression, DoMember, EmitDirective, EventCondition, EventDeclaration, EventFieldDecl, EventReference, GlobalParameterDeclaration, Identifier, IdentifierReference, IntegerLiteral, KeepConstraintDeclaration, LogicalExpression, MethodBody, NamedArgument, OnDirective, ParameterDeclaration, PhysicalLiteral, ParameterReference, PositionalArgument, RelationExpression, ScenarioInherits, SIUnitSpecifier, StringLiteral, ScenarioDeclaration, StructuredTypeExtension, Type, VariableDeclaration, WaitDirective, ListExpression + + +from ..osc2_parsing.OpenSCENARIO2Parser import OpenSCENARIO2Parser +from ..osc2_parsing.OpenSCENARIO2Listener import OpenSCENARIO2Listener + +from scenario_execution_base.model.error import OSC2ParsingError +import ast + +from antlr4.tree.Tree import ParseTreeWalker +import os +from pkg_resources import iter_entry_points, resource_filename + + +class ModelBuilder(OpenSCENARIO2Listener): # pylint: disable=too-many-public-methods + + def __init__(self, logger, parse_file_fct, file_name, log_model): + super().__init__() + self.__node_stack = [] + self.__cur_node = None + self.__current_label = None + self.model = None + self.logger = logger + self.imported_files = [] + self.parse_file = parse_file_fct + self.current_file = file_name + self.log_model = log_model + + def get_model(self): + return self.model + + # Enter a parse tree produced by OpenSCENARIO2Parser#osc_file. + def enterOsc_file(self, ctx: OpenSCENARIO2Parser.Osc_fileContext): + if self.model is None: # only on first file + node = CompilationUnit() + node.set_ctx(ctx, self.current_file) + + self.__node_stack.append(node) + self.__cur_node = node + self.model = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#osc_file. + def exitOsc_file(self, ctx: OpenSCENARIO2Parser.Osc_fileContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#preludeStatement. + def enterPreludeStatement(self, ctx: OpenSCENARIO2Parser.PreludeStatementContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#preludeStatement. + def exitPreludeStatement(self, ctx: OpenSCENARIO2Parser.PreludeStatementContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#importStatement. + def enterImportStatement(self, ctx: OpenSCENARIO2Parser.ImportStatementContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#importStatement. + def exitImportStatement(self, ctx: OpenSCENARIO2Parser.ImportStatementContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#importReference. + def enterImportReference(self, ctx: OpenSCENARIO2Parser.ImportReferenceContext): + file = None + if ctx.StringLiteral(): + file = ctx.getText() + if ctx.structuredIdentifier(): + import_reference = [] + for child in ctx.structuredIdentifier().getChildren(): + import_reference.append(child.getText()) + + self.logger.debug(f'import_reference is: {import_reference}') + if len(import_reference) < 3 or import_reference[0] != 'osc' or import_reference[1] != '.': + raise OSC2ParsingError( + msg=f'import_reference can only be of format osc., found "{import_reference}', context=ctx) + + # iterate through all packages + libraries_found = [] + for entry_point in iter_entry_points(group='scenario_execution.osc_libraries', name=None): + if entry_point.name == import_reference[2]: + libraries_found.append(entry_point) + if not libraries_found: + raise OSC2ParsingError( + msg=f'No import library "{".".join(import_reference)}" found.', context=ctx) + if len(libraries_found) > 1: + pkgs = [] + for elem in libraries_found: + try: + lib_class = elem.load() + resource, _ = lib_class() + pkgs.append(resource) + except ModuleNotFoundError: + pkgs.append('') + pass + + raise OSC2ParsingError( + msg=f'More than one import library for "{".".join(import_reference)}" found: {", ".join(pkgs)}', context=ctx) + + lib_class = libraries_found[0].load() + resource, filename = lib_class() + + lib_osc_dir = resource_filename(resource, 'lib_osc') + file = os.path.join(lib_osc_dir, filename) + + # Skip files that are already imported + if file in self.imported_files: + return + + imported_tree, errors = self.parse_file(file, self.log_model, f"{file} :") + + if errors: + raise OSC2ParsingError(msg=f'{errors} parsing errors found in import {file}.', context=ctx) + + walker = ParseTreeWalker() + prev_file = self.current_file + self.current_file = file + walker.walk(self, imported_tree) + self.current_file = prev_file + self.imported_files.append(import_reference) + + # Exit a parse tree produced by OpenSCENARIO2Parser#importReference. + def exitImportReference(self, ctx: OpenSCENARIO2Parser.ImportReferenceContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#structuredIdentifier. + def enterStructuredIdentifier(self, ctx: OpenSCENARIO2Parser.StructuredIdentifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structuredIdentifier. + def exitStructuredIdentifier(self, ctx: OpenSCENARIO2Parser.StructuredIdentifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#oscDeclaration. + def enterOscDeclaration(self, ctx: OpenSCENARIO2Parser.OscDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#oscDeclaration. + def exitOscDeclaration(self, ctx: OpenSCENARIO2Parser.OscDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#physicalTypeDeclaration. + def enterPhysicalTypeDeclaration(self, ctx: OpenSCENARIO2Parser.PhysicalTypeDeclarationContext): + self.__node_stack.append(self.__cur_node) + type_name = ctx.physicalTypeName().getText() + + node = PhysicalTypeDeclaration(type_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#physicalTypeDeclaration. + def exitPhysicalTypeDeclaration(self, ctx: OpenSCENARIO2Parser.PhysicalTypeDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#physicalTypeName. + def enterPhysicalTypeName(self, ctx: OpenSCENARIO2Parser.PhysicalTypeNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#physicalTypeName. + def exitPhysicalTypeName(self, ctx: OpenSCENARIO2Parser.PhysicalTypeNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#baseUnitSpecifier. + def enterBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.BaseUnitSpecifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#baseUnitSpecifier. + def exitBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.BaseUnitSpecifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#sIBaseUnitSpecifier. + def enterSIBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SIBaseUnitSpecifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#sIBaseUnitSpecifier. + def exitSIBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SIBaseUnitSpecifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#unitDeclaration. + def enterUnitDeclaration(self, ctx: OpenSCENARIO2Parser.UnitDeclarationContext): + self.__node_stack.append(self.__cur_node) + unit_name = ctx.unitName().getText() + + physical_name = ctx.physicalTypeName().getText() + + node = UnitDeclaration(unit_name, physical_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#unitDeclaration. + def exitUnitDeclaration(self, ctx: OpenSCENARIO2Parser.UnitDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#unitSpecifier. + def enterUnitSpecifier(self, ctx: OpenSCENARIO2Parser.UnitSpecifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#unitSpecifier. + def exitUnitSpecifier(self, ctx: OpenSCENARIO2Parser.UnitSpecifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#sIUnitSpecifier. + def enterSiUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SiUnitSpecifierContext): + factor = None + offset = None + if ctx.siFactor().FloatLiteral(): + factor = float(ctx.siFactor().FloatLiteral().getText()) + elif ctx.siFactor().integerLiteral(): + factor = int(ctx.siFactor().integerLiteral().getText()) + + if ctx.siOffset(): + if ctx.siOffset().FloatLiteral(): + offset = float(ctx.siOffset().FloatLiteral().getText()) + elif ctx.siOffset().integerLiteral(): + offset = int(ctx.siOffset().integerLiteral().getText()) + + node = SIUnitSpecifier(factor, offset) + self.__cur_node.set_children(node) + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumDeclaration. + def enterEnumDeclaration(self, ctx: OpenSCENARIO2Parser.EnumDeclarationContext): + self.__node_stack.append(self.__cur_node) + enum_name = ctx.enumName().getText() + + node = EnumDeclaration(enum_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumDeclaration. + def exitEnumDeclaration(self, ctx: OpenSCENARIO2Parser.EnumDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumMemberDecl. + def enterEnumMemberDecl(self, ctx: OpenSCENARIO2Parser.EnumMemberDeclContext): + self.__node_stack.append(self.__cur_node) + member_name = ctx.enumMemberName().getText() + + member_value = None + if ctx.enumMemberValue(): + if ctx.enumMemberValue().UintLiteral(): + member_value = int(ctx.enumMemberValue().UintLiteral().getText()) + elif ctx.enumMemberValue().HexUintLiteral(): + member_value = int(ctx.enumMemberValue().HexUintLiteral().getText(), 16) + else: + pass + + # The ModelElement enumeration value is the value stored in the symbol table + node = EnumMemberDeclaration(member_name, member_value) + # node.set_ctx(ctx, self.current_file) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumMemberDecl. + def exitEnumMemberDecl(self, ctx: OpenSCENARIO2Parser.EnumMemberDeclContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumMemberValue. + def enterEnumMemberValue(self, ctx: OpenSCENARIO2Parser.EnumMemberValueContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumMemberValue. + def exitEnumMemberValue(self, ctx: OpenSCENARIO2Parser.EnumMemberValueContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumName. + def enterEnumName(self, ctx: OpenSCENARIO2Parser.EnumNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumName. + def exitEnumName(self, ctx: OpenSCENARIO2Parser.EnumNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumMemberName. + def enterEnumMemberName(self, ctx: OpenSCENARIO2Parser.EnumMemberNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumMemberName. + def exitEnumMemberName(self, ctx: OpenSCENARIO2Parser.EnumMemberNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumValueReference. + def enterEnumValueReference(self, ctx: OpenSCENARIO2Parser.EnumValueReferenceContext): + self.__node_stack.append(self.__cur_node) + enum_name = None + if ctx.enumName(): + enum_name = ctx.enumName().getText() + + enum_member_name = ctx.enumMemberName().getText() + + node = EnumValueReference(enum_name, enum_member_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumValueReference. + def exitEnumValueReference(self, ctx: OpenSCENARIO2Parser.EnumValueReferenceContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#inheritsCondition. + def enterInheritsCondition(self, ctx: OpenSCENARIO2Parser.InheritsConditionContext): + raise OSC2ParsingError(msg=f"inherits condition not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # field_name = ctx.fieldName().getText() + + # # Since there is no function to process bool_literal, + # # so we manually add a bool literal node to tree. + # bool_literal_node = None + # if ctx.BoolLiteral(): + # bool_literal = ctx.BoolLiteral().getText() + # bool_literal_node = BoolLiteral(bool_literal) + + # node = InheritsCondition(field_name, bool_literal_node) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#inheritsCondition. + def exitInheritsCondition(self, ctx: OpenSCENARIO2Parser.InheritsConditionContext): + self.__cur_node = self.__node_stack.pop() + # Enter a parse tree produced by OpenSCENARIO2Parser#structDeclaration. + + def enterStructDeclaration(self, ctx: OpenSCENARIO2Parser.StructDeclarationContext): + self.__node_stack.append(self.__cur_node) + struct_name = ctx.structName().getText() + + node = StructDeclaration(struct_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#structDeclaration. + def exitStructDeclaration(self, ctx: OpenSCENARIO2Parser.StructDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#structInherits. + def enterStructInherits(self, ctx: OpenSCENARIO2Parser.StructInheritsContext): + self.__node_stack.append(self.__cur_node) + struct_name = ctx.structName().getText() + + node = StructInherits(struct_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#structInherits. + def exitStructInherits(self, ctx: OpenSCENARIO2Parser.StructInheritsContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#structMemberDecl. + def enterStructMemberDecl(self, ctx: OpenSCENARIO2Parser.StructMemberDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structMemberDecl. + def exitStructMemberDecl(self, ctx: OpenSCENARIO2Parser.StructMemberDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#fieldName. + def enterFieldName(self, ctx: OpenSCENARIO2Parser.FieldNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#fieldName. + def exitFieldName(self, ctx: OpenSCENARIO2Parser.FieldNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#structName. + def enterStructName(self, ctx: OpenSCENARIO2Parser.StructNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structName. + def exitStructName(self, ctx: OpenSCENARIO2Parser.StructNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorDeclaration. + def enterActorDeclaration(self, ctx: OpenSCENARIO2Parser.ActorDeclarationContext): + self.__node_stack.append(self.__cur_node) + actor_name = ctx.actorName().getText() + + node = ActorDeclaration(actor_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorDeclaration. + def exitActorDeclaration(self, ctx: OpenSCENARIO2Parser.ActorDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorInherits. + def enterActorInherits(self, ctx: OpenSCENARIO2Parser.ActorInheritsContext): + self.__node_stack.append(self.__cur_node) + actor_name = ctx.actorName().getText() + + node = ActorInherits(actor_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorInherits. + def exitActorInherits(self, ctx: OpenSCENARIO2Parser.ActorInheritsContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorMemberDecl. + def enterActorMemberDecl(self, ctx: OpenSCENARIO2Parser.ActorMemberDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorMemberDecl. + def exitActorMemberDecl(self, ctx: OpenSCENARIO2Parser.ActorMemberDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorName. + def enterActorName(self, ctx: OpenSCENARIO2Parser.ActorNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorName. + def exitActorName(self, ctx: OpenSCENARIO2Parser.ActorNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#scenarioDeclaration. + def enterScenarioDeclaration(self, ctx: OpenSCENARIO2Parser.ScenarioDeclarationContext): + self.__node_stack.append(self.__cur_node) + qualified_behavior_name = ctx.qualifiedBehaviorName().getText() + + node = ScenarioDeclaration(qualified_behavior_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#scenarioDeclaration. + def exitScenarioDeclaration(self, ctx: OpenSCENARIO2Parser.ScenarioDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#scenarioInherits. + def enterScenarioInherits(self, ctx: OpenSCENARIO2Parser.ScenarioInheritsContext): + self.__node_stack.append(self.__cur_node) + qualified_behavior_name = ctx.qualifiedBehaviorName().getText() + + node = ScenarioInherits(qualified_behavior_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#scenarioInherits. + def exitScenarioInherits(self, ctx: OpenSCENARIO2Parser.ScenarioInheritsContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#scenarioMemberDecl. + def enterScenarioMemberDecl(self, ctx: OpenSCENARIO2Parser.ScenarioMemberDeclContext): + self.__node_stack.append(self.__cur_node) + + # Exit a parse tree produced by OpenSCENARIO2Parser#scenarioMemberDecl. + def exitScenarioMemberDecl(self, ctx: OpenSCENARIO2Parser.ScenarioMemberDeclContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#qualifiedBehaviorName. + def enterQualifiedBehaviorName(self, ctx: OpenSCENARIO2Parser.QualifiedBehaviorNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#qualifiedBehaviorName. + def exitQualifiedBehaviorName(self, ctx: OpenSCENARIO2Parser.QualifiedBehaviorNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorName. + def enterBehaviorName(self, ctx: OpenSCENARIO2Parser.BehaviorNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorName. + def exitBehaviorName(self, ctx: OpenSCENARIO2Parser.BehaviorNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actionDeclaration. + def enterActionDeclaration(self, ctx: OpenSCENARIO2Parser.ActionDeclarationContext): + self.__node_stack.append(self.__cur_node) + qualified_behavior_name = ctx.qualifiedBehaviorName().getText() + node = ActionDeclaration(qualified_behavior_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#actionDeclaration. + def exitActionDeclaration(self, ctx: OpenSCENARIO2Parser.ActionDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#actionInherits. + def enterActionInherits(self, ctx: OpenSCENARIO2Parser.ActionInheritsContext): + self.__node_stack.append(self.__cur_node) + qualified_behavior_name = ctx.qualifiedBehaviorName().getText() + + node = ActionInherits(qualified_behavior_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#actionInherits. + def exitActionInherits(self, ctx: OpenSCENARIO2Parser.ActionInheritsContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#modifierDeclaration. + def enterModifierDeclaration(self, ctx: OpenSCENARIO2Parser.ModifierDeclarationContext): + raise OSC2ParsingError(msg=f"modifier declaration not supported yet.", context=ctx) + # self.__node_stack.append(self.__cur_node) + # actor_name = None + # if ctx.actorName(): + # actor_name = ctx.actorName().getText() + + # modifier_name = ctx.modifierName().getText() + + # node = ModifierDeclaration(actor_name, modifier_name) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#modifierDeclaration. + def exitModifierDeclaration(self, ctx: OpenSCENARIO2Parser.ModifierDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#modifierName. + def enterModifierName(self, ctx: OpenSCENARIO2Parser.ModifierNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#modifierName. + def exitModifierName(self, ctx: OpenSCENARIO2Parser.ModifierNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#typeExtension. + def enterTypeExtension(self, ctx: OpenSCENARIO2Parser.TypeExtensionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#typeExtension. + def exitTypeExtension(self, ctx: OpenSCENARIO2Parser.TypeExtensionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumTypeExtension. + def enterEnumTypeExtension(self, ctx: OpenSCENARIO2Parser.EnumTypeExtensionContext): + raise OSC2ParsingError(msg=f"enum type extension not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # enum_name = ctx.enumName().getText() + + # node = EnumTypeExtension(enum_name) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumTypeExtension. + def exitEnumTypeExtension(self, ctx: OpenSCENARIO2Parser.EnumTypeExtensionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#structuredTypeExtension. + def enterStructuredTypeExtension(self, ctx: OpenSCENARIO2Parser.StructuredTypeExtensionContext): + self.__node_stack.append(self.__cur_node) + + type_name = None + if ctx.extendableTypeName().typeName(): + type_name = ctx.extendableTypeName().typeName().getText() + # Even if a symbol table with the corresponding name is found, + # it is necessary to determine whether the extended symbol table matches the original type + + # The two ifs here are syntactically mutually exclusive + qualified_behavior_name = None + if ctx.extendableTypeName().qualifiedBehaviorName(): + qualified_behavior_name = ( + ctx.extendableTypeName().qualifiedBehaviorName().getText() + ) + + node = StructuredTypeExtension(type_name, qualified_behavior_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#structuredTypeExtension. + def exitStructuredTypeExtension(self, ctx: OpenSCENARIO2Parser.StructuredTypeExtensionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#extendableTypeName. + def enterExtendableTypeName(self, ctx: OpenSCENARIO2Parser.ExtendableTypeNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#extendableTypeName. + def exitExtendableTypeName(self, ctx: OpenSCENARIO2Parser.ExtendableTypeNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#extensionMemberDecl. + def enterExtensionMemberDecl(self, ctx: OpenSCENARIO2Parser.ExtensionMemberDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#extensionMemberDecl. + def exitExtensionMemberDecl(self, ctx: OpenSCENARIO2Parser.ExtensionMemberDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#globalParameterDeclaration. + def enterGlobalParameterDeclaration(self, ctx: OpenSCENARIO2Parser.GlobalParameterDeclarationContext): + default_value = None + field_type = ctx.typeDeclarator().getText() + self.__node_stack.append(self.__cur_node) + if len(ctx.fieldName()) != 1: + raise OSC2ParsingError(msg="Only single field names are currently supported", context=ctx) + field_name = ctx.fieldName()[0].Identifier().getText() + # TODO field_name = [] + # multi_field_name = "" + # for fn in ctx.fieldName(): + # name = fn.Identifier().getText() + # if name in field_name: + # raise OSC2ParsingError( + # msg= "Can not define same param in same scope!", + # line=ctx.start.line, + # column=ctx.start.column, + # context=ctx.getText() + # ) + # field_name.append(name) + # multi_field_name = multi_field_name_append(multi_field_name, name) + + node = GlobalParameterDeclaration(field_name, field_name, field_type, default_value) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#globalParameterDeclaration. + def exitGlobalParameterDeclaration(self, ctx: OpenSCENARIO2Parser.GlobalParameterDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#typeDeclarator. + def enterTypeDeclarator(self, ctx: OpenSCENARIO2Parser.TypeDeclaratorContext): + self.__node_stack.append(self.__cur_node) + type_name = None + is_list = False + if ctx.nonAggregateTypeDeclarator(): + type_name = ctx.nonAggregateTypeDeclarator().getText() + if type_name.startswith('listof'): + raise OSC2ParsingError(msg=f"Type name starting with listof is not allowed.", context=ctx) + elif ctx.aggregateTypeDeclarator(): + is_list = True + type_name = ctx.aggregateTypeDeclarator().getText() + + node = Type(type_name, is_list) + + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#typeDeclarator. + def exitTypeDeclarator(self, ctx: OpenSCENARIO2Parser.TypeDeclaratorContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#nonAggregateTypeDeclarator. + def enterNonAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.NonAggregateTypeDeclaratorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#nonAggregateTypeDeclarator. + def exitNonAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.NonAggregateTypeDeclaratorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#aggregateTypeDeclarator. + def enterAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.AggregateTypeDeclaratorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#aggregateTypeDeclarator. + def exitAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.AggregateTypeDeclaratorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#listTypeDeclarator. + def enterListTypeDeclarator(self, ctx: OpenSCENARIO2Parser.ListTypeDeclaratorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#listTypeDeclarator. + def exitListTypeDeclarator(self, ctx: OpenSCENARIO2Parser.ListTypeDeclaratorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#primitiveType. + def enterPrimitiveType(self, ctx: OpenSCENARIO2Parser.PrimitiveTypeContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#primitiveType. + def exitPrimitiveType(self, ctx: OpenSCENARIO2Parser.PrimitiveTypeContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#typeName. + def enterTypeName(self, ctx: OpenSCENARIO2Parser.TypeNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#typeName. + def exitTypeName(self, ctx: OpenSCENARIO2Parser.TypeNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventDeclaration. + def enterEventDeclaration(self, ctx: OpenSCENARIO2Parser.EventDeclarationContext): + self.__node_stack.append(self.__cur_node) + event_name = ctx.eventName().getText() + + node = EventDeclaration(event_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventDeclaration. + def exitEventDeclaration(self, ctx: OpenSCENARIO2Parser.EventDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventSpecification. + def enterEventSpecification(self, ctx: OpenSCENARIO2Parser.EventSpecificationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventSpecification. + def exitEventSpecification(self, ctx: OpenSCENARIO2Parser.EventSpecificationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventReference. + def enterEventReference(self, ctx: OpenSCENARIO2Parser.EventReferenceContext): + self.__node_stack.append(self.__cur_node) + event_path = ctx.eventPath().getText() + node = EventReference(event_path) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventReference. + def exitEventReference(self, ctx: OpenSCENARIO2Parser.EventReferenceContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventFieldDecl. + def enterEventFieldDecl(self, ctx: OpenSCENARIO2Parser.EventFieldDeclContext): + self.__node_stack.append(self.__cur_node) + event_field_name = ctx.eventFieldName().getText() + node = EventFieldDecl(event_field_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventFieldDecl. + def exitEventFieldDecl(self, ctx: OpenSCENARIO2Parser.EventFieldDeclContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventFieldName. + def enterEventFieldName(self, ctx: OpenSCENARIO2Parser.EventFieldNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventFieldName. + def exitEventFieldName(self, ctx: OpenSCENARIO2Parser.EventFieldNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventName. + def enterEventName(self, ctx: OpenSCENARIO2Parser.EventNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventName. + def exitEventName(self, ctx: OpenSCENARIO2Parser.EventNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventPath. + def enterEventPath(self, ctx: OpenSCENARIO2Parser.EventPathContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventPath. + def exitEventPath(self, ctx: OpenSCENARIO2Parser.EventPathContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventCondition. + def enterEventCondition(self, ctx: OpenSCENARIO2Parser.EventConditionContext): + self.__node_stack.append(self.__cur_node) + node = EventCondition() + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventCondition. + def exitEventCondition(self, ctx: OpenSCENARIO2Parser.EventConditionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#riseExpression. + def enterRiseExpression(self, ctx: OpenSCENARIO2Parser.RiseExpressionContext): + raise OSC2ParsingError(msg=f"rise expression not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # node = RiseExpression() + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#riseExpression. + def exitRiseExpression(self, ctx: OpenSCENARIO2Parser.RiseExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#fallExpression. + def enterFallExpression(self, ctx: OpenSCENARIO2Parser.FallExpressionContext): + raise OSC2ParsingError(msg=f"fall expression not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # node = FallExpression() + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#fallExpression. + def exitFallExpression(self, ctx: OpenSCENARIO2Parser.FallExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#elapsedExpression. + def enterElapsedExpression(self, ctx: OpenSCENARIO2Parser.ElapsedExpressionContext): + self.__node_stack.append(self.__cur_node) + node = ElapsedExpression() + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#elapsedExpression. + def exitElapsedExpression(self, ctx: OpenSCENARIO2Parser.ElapsedExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#everyExpression. + def enterEveryExpression(self, ctx: OpenSCENARIO2Parser.EveryExpressionContext): + raise OSC2ParsingError(msg=f"every expression not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # node = EveryExpression() + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#everyExpression. + def exitEveryExpression(self, ctx: OpenSCENARIO2Parser.EveryExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#boolExpression. + def enterBoolExpression(self, ctx: OpenSCENARIO2Parser.BoolExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#boolExpression. + def exitBoolExpression(self, ctx: OpenSCENARIO2Parser.BoolExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#durationExpression. + def enterDurationExpression(self, ctx: OpenSCENARIO2Parser.DurationExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#durationExpression. + def exitDurationExpression(self, ctx: OpenSCENARIO2Parser.DurationExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#fieldDeclaration. + def enterFieldDeclaration(self, ctx: OpenSCENARIO2Parser.FieldDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#fieldDeclaration. + def exitFieldDeclaration(self, ctx: OpenSCENARIO2Parser.FieldDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#parameterDeclaration. + def enterParameterDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterDeclarationContext): + default_value = None + if ctx.defaultValue(): + default_value = ctx.defaultValue().getText() + field_type = ctx.typeDeclarator().getText() + self.__node_stack.append(self.__cur_node) + + if len(ctx.fieldName()) != 1: + raise OSC2ParsingError(msg="Only single field names are currently supported", context=ctx) + field_name = ctx.fieldName()[0].Identifier().getText() + + # TODO field_name = [] + # for fn in ctx.fieldName(): + # name = fn.Identifier().getText() + # if name in field_name: + # raise OSC2ParsingError( + # msg= "Can not define same param in same scope!", + # line=ctx.start.line, + # column=ctx.start.column, + # context=ctx.getText() + # ) + # field_name.append(name) + + node = ParameterDeclaration(field_name, field_type, default_value) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#parameterDeclaration. + def exitParameterDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#variableDeclaration. + def enterVariableDeclaration(self, ctx: OpenSCENARIO2Parser.VariableDeclarationContext): + self.__node_stack.append(self.__cur_node) + field_name = [] + default_value = None + if ctx.sampleExpression(): + default_value = ctx.sampleExpression().getText() + elif ctx.valueExp(): + if ctx.valueExp().listConstructor(): + default_value = ast.literal_eval(ctx.valueExp().getText()) + else: + default_value = ctx.valueExp().getText() + + # TODO multi_field_name = "" + # for fn in ctx.fieldName(): + # name = fn.Identifier().getText() + # if name in field_name: + # raise OSC2ParsingError(msg="Can not define same param in same scope!", context=ctx) + # field_name.append(name) + # multi_field_name = multi_field_name_append(multi_field_name, name) + + field_type = ctx.typeDeclarator().getText() + + node = VariableDeclaration(field_name, field_type, default_value) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#variableDeclaration. + def exitVariableDeclaration(self, ctx: OpenSCENARIO2Parser.VariableDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#sampleExpression. + def enterSampleExpression(self, ctx: OpenSCENARIO2Parser.SampleExpressionContext): + raise OSC2ParsingError(msg=f"sample expression not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # node = SampleExpression() + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#sampleExpression. + def exitSampleExpression(self, ctx: OpenSCENARIO2Parser.SampleExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#defaultValue. + def enterDefaultValue(self, ctx: OpenSCENARIO2Parser.DefaultValueContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#defaultValue. + def exitDefaultValue(self, ctx: OpenSCENARIO2Parser.DefaultValueContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#parameterWithDeclaration. + def enterParameterWithDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterWithDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#parameterWithDeclaration. + def exitParameterWithDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterWithDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#parameterWithMember. + def enterParameterWithMember(self, ctx: OpenSCENARIO2Parser.ParameterWithMemberContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#parameterWithMember. + def exitParameterWithMember(self, ctx: OpenSCENARIO2Parser.ParameterWithMemberContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#constraintDeclaration. + def enterConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.ConstraintDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#constraintDeclaration. + def exitConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.ConstraintDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#keepConstraintDeclaration. + def enterKeepConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.KeepConstraintDeclarationContext): + self.__node_stack.append(self.__cur_node) + constraint_qualifier = None + if ctx.constraintQualifier(): + constraint_qualifier = ctx.constraintQualifier().getText() + + node = KeepConstraintDeclaration(constraint_qualifier) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#keepConstraintDeclaration. + def exitKeepConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.KeepConstraintDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#constraintQualifier. + def enterConstraintQualifier(self, ctx: OpenSCENARIO2Parser.ConstraintQualifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#constraintQualifier. + def exitConstraintQualifier(self, ctx: OpenSCENARIO2Parser.ConstraintQualifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#constraintExpression. + def enterConstraintExpression(self, ctx: OpenSCENARIO2Parser.ConstraintExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#constraintExpression. + def exitConstraintExpression(self, ctx: OpenSCENARIO2Parser.ConstraintExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#removeDefaultDeclaration. + def enterRemoveDefaultDeclaration(self, ctx: OpenSCENARIO2Parser.RemoveDefaultDeclarationContext): + raise OSC2ParsingError(msg=f"remove default declaration not supported yet.", context=ctx) + # self.__node_stack.append(self.__cur_node) + # node = RemoveDefaultDeclaration() + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#removeDefaultDeclaration. + def exitRemoveDefaultDeclaration(self, ctx: OpenSCENARIO2Parser.RemoveDefaultDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#parameterReference. + def enterParameterReference(self, ctx: OpenSCENARIO2Parser.ParameterReferenceContext): + self.__node_stack.append(self.__cur_node) + field_name = None + if ctx.fieldName(): + field_name = ctx.fieldName().getText() + + field_access = None + if ctx.fieldAccess(): + field_access = ctx.fieldAccess().getText() + + node = ParameterReference(field_name, field_access) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#parameterReference. + def exitParameterReference(self, ctx: OpenSCENARIO2Parser.ParameterReferenceContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#modifierInvocation. + def enterModifierInvocation(self, ctx: OpenSCENARIO2Parser.ModifierInvocationContext): + raise OSC2ParsingError(msg=f"modifier invocation not supported yet.", context=ctx) + # self.__node_stack.append(self.__cur_node) + # modifier_name = ctx.modifierName().getText() + + # actor = None + # if ctx.actorExpression(): + # actor = ctx.actorExpression().getText() + + # if ctx.behaviorExpression(): + # actor = ctx.behaviorExpression().getText() + + # node = ModifierInvocation(actor, modifier_name) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#modifierInvocation. + def exitModifierInvocation(self, ctx: OpenSCENARIO2Parser.ModifierInvocationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorExpression. + def enterBehaviorExpression(self, ctx: OpenSCENARIO2Parser.BehaviorExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorExpression. + def exitBehaviorExpression(self, ctx: OpenSCENARIO2Parser.BehaviorExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorSpecification. + def enterBehaviorSpecification(self, ctx: OpenSCENARIO2Parser.BehaviorSpecificationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorSpecification. + def exitBehaviorSpecification(self, ctx: OpenSCENARIO2Parser.BehaviorSpecificationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#onDirective. + def enterOnDirective(self, ctx: OpenSCENARIO2Parser.OnDirectiveContext): + self.__node_stack.append(self.__cur_node) + node = OnDirective() + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#onDirective. + def exitOnDirective(self, ctx: OpenSCENARIO2Parser.OnDirectiveContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#onMember. + def enterOnMember(self, ctx: OpenSCENARIO2Parser.OnMemberContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#onMember. + def exitOnMember(self, ctx: OpenSCENARIO2Parser.OnMemberContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#doDirective. + def enterDoDirective(self, ctx: OpenSCENARIO2Parser.DoDirectiveContext): + self.__node_stack.append(self.__cur_node) + + node = DoDirective() + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#doDirective. + def exitDoDirective(self, ctx: OpenSCENARIO2Parser.DoDirectiveContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#doMember. + def enterDoMember(self, ctx: OpenSCENARIO2Parser.DoMemberContext): + self.__node_stack.append(self.__cur_node) + self.__current_label = None + + if ctx.labelName(): + self.__current_label = ctx.labelName().getText() + + composition_operator = None + if ctx.composition(): + composition_operator = ctx.composition().compositionOperator().getText().strip("\'").strip("\"") + + if composition_operator is not None: + node = DoMember(self.__current_label, composition_operator) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#doMember. + def exitDoMember(self, ctx: OpenSCENARIO2Parser.DoMemberContext): + self.__cur_node = self.__node_stack.pop() + self.__current_label = None + + # Enter a parse tree produced by OpenSCENARIO2Parser#composition. + def enterComposition(self, ctx: OpenSCENARIO2Parser.CompositionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#composition. + def exitComposition(self, ctx: OpenSCENARIO2Parser.CompositionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#compositionOperator. + def enterCompositionOperator(self, ctx: OpenSCENARIO2Parser.CompositionOperatorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#compositionOperator. + def exitCompositionOperator(self, ctx: OpenSCENARIO2Parser.CompositionOperatorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorInvocation. + def enterBehaviorInvocation(self, ctx: OpenSCENARIO2Parser.BehaviorInvocationContext): + self.__node_stack.append(self.__cur_node) + actor = None + name = "" + behavior_name = ctx.behaviorName().getText() + if ctx.actorExpression(): + actor = ctx.actorExpression().getText() + name += actor + "." + + name += behavior_name + + node = BehaviorInvocation(self.__current_label, actor, behavior_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorInvocation. + def exitBehaviorInvocation(self, ctx: OpenSCENARIO2Parser.BehaviorInvocationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorWithDeclaration. + def enterBehaviorWithDeclaration(self, ctx: OpenSCENARIO2Parser.BehaviorWithDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorWithDeclaration. + def exitBehaviorWithDeclaration(self, ctx: OpenSCENARIO2Parser.BehaviorWithDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorWithMember. + def enterBehaviorWithMember(self, ctx: OpenSCENARIO2Parser.BehaviorWithMemberContext): + self.__node_stack.append(self.__cur_node) + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorWithMember. + def exitBehaviorWithMember(self, ctx: OpenSCENARIO2Parser.BehaviorWithMemberContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#labelName. + def enterLabelName(self, ctx: OpenSCENARIO2Parser.LabelNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#labelName. + def exitLabelName(self, ctx: OpenSCENARIO2Parser.LabelNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorExpression. + def enterActorExpression(self, ctx: OpenSCENARIO2Parser.ActorExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorExpression. + def exitActorExpression(self, ctx: OpenSCENARIO2Parser.ActorExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#waitDirective. + def enterWaitDirective(self, ctx: OpenSCENARIO2Parser.WaitDirectiveContext): + self.__node_stack.append(self.__cur_node) + + node = WaitDirective() + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#waitDirective. + def exitWaitDirective(self, ctx: OpenSCENARIO2Parser.WaitDirectiveContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#emitDirective. + def enterEmitDirective(self, ctx: OpenSCENARIO2Parser.EmitDirectiveContext): + self.__node_stack.append(self.__cur_node) + event_name = ctx.eventName().getText() + node = EmitDirective(event_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#emitDirective. + def exitEmitDirective(self, ctx: OpenSCENARIO2Parser.EmitDirectiveContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#callDirective. + def enterCallDirective(self, ctx: OpenSCENARIO2Parser.CallDirectiveContext): + raise OSC2ParsingError(msg=f"call directive not supported yet.", context=ctx) + # self.__node_stack.append(self.__cur_node) + # method_name = ctx.methodInvocation().postfixExp().getText() + + # node = CallDirective(method_name) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#callDirective. + def exitCallDirective(self, ctx: OpenSCENARIO2Parser.CallDirectiveContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#untilDirective. + def enterUntilDirective(self, ctx: OpenSCENARIO2Parser.UntilDirectiveContext): + raise OSC2ParsingError(msg=f"until declaration not supported yet.", context=ctx) + # self.__node_stack.append(self.__cur_node) + # node = UntilDirective() + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#untilDirective. + def exitUntilDirective(self, ctx: OpenSCENARIO2Parser.UntilDirectiveContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodInvocation. + def enterMethodInvocation(self, ctx: OpenSCENARIO2Parser.MethodInvocationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodInvocation. + def exitMethodInvocation(self, ctx: OpenSCENARIO2Parser.MethodInvocationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodDeclaration. + def enterMethodDeclaration(self, ctx: OpenSCENARIO2Parser.MethodDeclarationContext): + raise OSC2ParsingError(msg=f"method declaration not supported yet.", context=ctx) + # self.__node_stack.append(self.__cur_node) + # method_name = ctx.methodName().getText() + # return_type = None + # if ctx.returnType(): + # return_type = ctx.returnType().getText() + + # node = MethodDeclaration(method_name, return_type) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodDeclaration. + def exitMethodDeclaration(self, ctx: OpenSCENARIO2Parser.MethodDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#returnType. + def enterReturnType(self, ctx: OpenSCENARIO2Parser.ReturnTypeContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#returnType. + def exitReturnType(self, ctx: OpenSCENARIO2Parser.ReturnTypeContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodImplementation. + def enterMethodImplementation(self, ctx: OpenSCENARIO2Parser.MethodImplementationContext): + self.__node_stack.append(self.__cur_node) + qualifier = None + if ctx.methodQualifier(): + qualifier = ctx.methodQualifier().getText() + + if ctx.expression(): + _type = "expression" + elif ctx.structuredIdentifier(): + _type = "external" + else: + _type = "undefined" + + external_name = None + if ctx.structuredIdentifier(): + external_name = ctx.structuredIdentifier().getText() + + node = MethodBody(qualifier, _type, external_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodImplementation. + def exitMethodImplementation(self, ctx: OpenSCENARIO2Parser.MethodImplementationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodQualifier. + def enterMethodQualifier(self, ctx: OpenSCENARIO2Parser.MethodQualifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodQualifier. + def exitMethodQualifier(self, ctx: OpenSCENARIO2Parser.MethodQualifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodName. + def enterMethodName(self, ctx: OpenSCENARIO2Parser.MethodNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodName. + def exitMethodName(self, ctx: OpenSCENARIO2Parser.MethodNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageDeclaration. + def enterCoverageDeclaration(self, ctx: OpenSCENARIO2Parser.CoverageDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageDeclaration. + def exitCoverageDeclaration(self, ctx: OpenSCENARIO2Parser.CoverageDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverDeclaration. + def enterCoverDeclaration(self, ctx: OpenSCENARIO2Parser.CoverDeclarationContext): + raise OSC2ParsingError(msg=f"cover declaration not supported yet.", context=ctx) + # self.__node_stack.append(self.__cur_node) + # target_name = None + # if ctx.targetName(): + # target_name = ctx.targetName().getText() + + # node = CoverDeclaration(target_name) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverDeclaration. + def exitCoverDeclaration(self, ctx: OpenSCENARIO2Parser.CoverDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#recordDeclaration. + def enterRecordDeclaration(self, ctx: OpenSCENARIO2Parser.RecordDeclarationContext): + raise OSC2ParsingError(msg=f"record declaration not supported yet.", context=ctx) + # self.__node_stack.append(self.__cur_node) + # target_name = None + # if ctx.targetName(): + # target_name = ctx.targetName().getText() + + # node = RecordDeclaration(target_name) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#recordDeclaration. + def exitRecordDeclaration(self, ctx: OpenSCENARIO2Parser.RecordDeclarationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageExpression. + def enterCoverageExpression(self, ctx: OpenSCENARIO2Parser.CoverageExpressionContext): + self.__node_stack.append(self.__cur_node) + argument_name = "expression" + node = NamedArgument(argument_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageExpression. + def exitCoverageExpression(self, ctx: OpenSCENARIO2Parser.CoverageExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageUnit. + def enterCoverageUnit(self, ctx: OpenSCENARIO2Parser.CoverageUnitContext): + self.__node_stack.append(self.__cur_node) + argument_name = "unit" + node = NamedArgument(argument_name) + node.set_ctx(ctx, self.current_file) + + unit_name = Identifier(ctx.Identifier().getText()) + unit_name.set_loc(ctx.start.line, ctx.start.column) + node.set_children(unit_name) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageUnit. + def exitCoverageUnit(self, ctx: OpenSCENARIO2Parser.CoverageUnitContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageRange. + def enterCoverageRange(self, ctx: OpenSCENARIO2Parser.CoverageRangeContext): + self.__node_stack.append(self.__cur_node) + argument_name = "range" + node = NamedArgument(argument_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageRange. + def exitCoverageRange(self, ctx: OpenSCENARIO2Parser.CoverageRangeContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageEvery. + def enterCoverageEvery(self, ctx: OpenSCENARIO2Parser.CoverageEveryContext): + self.__node_stack.append(self.__cur_node) + argument_name = "every" + node = NamedArgument(argument_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageEvery. + def exitCoverageEvery(self, ctx: OpenSCENARIO2Parser.CoverageEveryContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageEvent. + def enterCoverageEvent(self, ctx: OpenSCENARIO2Parser.CoverageEventContext): + self.__node_stack.append(self.__cur_node) + argument_name = "event" + node = NamedArgument(argument_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageEvent. + def exitCoverageEvent(self, ctx: OpenSCENARIO2Parser.CoverageEventContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageNameArgument. + def enterCoverageNameArgument(self, ctx: OpenSCENARIO2Parser.CoverageNameArgumentContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageNameArgument. + def exitCoverageNameArgument(self, ctx: OpenSCENARIO2Parser.CoverageNameArgumentContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#targetName. + def enterTargetName(self, ctx: OpenSCENARIO2Parser.TargetNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#targetName. + def exitTargetName(self, ctx: OpenSCENARIO2Parser.TargetNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#expression. + def enterExpression(self, ctx: OpenSCENARIO2Parser.ExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#expression. + def exitExpression(self, ctx: OpenSCENARIO2Parser.ExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#ternaryOpExp. + def enterTernaryOpExp(self, ctx: OpenSCENARIO2Parser.TernaryOpExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#ternaryOpExp. + def exitTernaryOpExp(self, ctx: OpenSCENARIO2Parser.TernaryOpExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#implication. + def enterImplication(self, ctx: OpenSCENARIO2Parser.ImplicationContext): + self.__node_stack.append(self.__cur_node) + if len(ctx.disjunction()) > 1: + operator = "=>" + node = LogicalExpression(operator) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#implication. + def exitImplication(self, ctx: OpenSCENARIO2Parser.ImplicationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#disjunction. + def enterDisjunction(self, ctx: OpenSCENARIO2Parser.DisjunctionContext): + self.__node_stack.append(self.__cur_node) + if len(ctx.conjunction()) > 1: + operator = "or" + node = LogicalExpression(operator) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#disjunction. + def exitDisjunction(self, ctx: OpenSCENARIO2Parser.DisjunctionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#conjunction. + def enterConjunction(self, ctx: OpenSCENARIO2Parser.ConjunctionContext): + self.__node_stack.append(self.__cur_node) + if len(ctx.inversion()) > 1: + operator = "and" + node = LogicalExpression(operator) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#conjunction. + def exitConjunction(self, ctx: OpenSCENARIO2Parser.ConjunctionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#inversion. + def enterInversion(self, ctx: OpenSCENARIO2Parser.InversionContext): + self.__node_stack.append(self.__cur_node) + if ctx.relation() is None: + operator = "not" + node = LogicalExpression(operator) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#inversion. + def exitInversion(self, ctx: OpenSCENARIO2Parser.InversionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#relation. + def enterRelation(self, ctx: OpenSCENARIO2Parser.RelationContext): # pylint: disable=invalid-name + self.__node_stack.append(self.__cur_node) + + # Exit a parse tree produced by OpenSCENARIO2Parser#relation. + def exitRelation(self, ctx: OpenSCENARIO2Parser.RelationContext): # pylint: disable=invalid-name + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#relationExp. + def enterRelationExp(self, ctx: OpenSCENARIO2Parser.RelationExpContext): + self.__node_stack.append(self.__cur_node) + operator = ctx.relationalOp().getText() + node = RelationExpression(operator) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#relationExp. + def exitRelationExp(self, ctx: OpenSCENARIO2Parser.RelationExpContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#relationalOp. + def enterRelationalOp(self, ctx: OpenSCENARIO2Parser.RelationalOpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#relationalOp. + def exitRelationalOp(self, ctx: OpenSCENARIO2Parser.RelationalOpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#sum. + def enterSumExpression(self, ctx: OpenSCENARIO2Parser.SumExpressionContext): # pylint: disable=invalid-name + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#sum. + def exitSumExpression(self, ctx: OpenSCENARIO2Parser.SumExpressionContext): # pylint: disable=invalid-name + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#additiveExp. + def enterAdditiveExp(self, ctx: OpenSCENARIO2Parser.AdditiveExpContext): + self.__node_stack.append(self.__cur_node) + operator = ctx.additiveOp().getText() + node = BinaryExpression(operator) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#additiveExp. + def exitAdditiveExp(self, ctx: OpenSCENARIO2Parser.AdditiveExpContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#additiveOp. + def enterAdditiveOp(self, ctx: OpenSCENARIO2Parser.AdditiveOpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#additiveOp. + def exitAdditiveOp(self, ctx: OpenSCENARIO2Parser.AdditiveOpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#multiplicativeExp. + def enterMultiplicativeExp(self, ctx: OpenSCENARIO2Parser.MultiplicativeExpContext): + self.__node_stack.append(self.__cur_node) + operator = ctx.multiplicativeOp().getText() + node = BinaryExpression(operator) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#multiplicativeExp. + def exitMultiplicativeExp(self, ctx: OpenSCENARIO2Parser.MultiplicativeExpContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#term. + def enterTerm(self, ctx: OpenSCENARIO2Parser.TermContext): # pylint: disable=invalid-name + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#term. + def exitTerm(self, ctx: OpenSCENARIO2Parser.TermContext): # pylint: disable=invalid-name + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#multiplicativeOp. + def enterMultiplicativeOp(self, ctx: OpenSCENARIO2Parser.MultiplicativeOpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#multiplicativeOp. + def exitMultiplicativeOp(self, ctx: OpenSCENARIO2Parser.MultiplicativeOpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#factor. + def enterFactor(self, ctx: OpenSCENARIO2Parser.FactorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#factor. + def exitFactor(self, ctx: OpenSCENARIO2Parser.FactorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#primaryExpression. + def enterPrimaryExpression(self, ctx: OpenSCENARIO2Parser.PrimaryExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#primaryExpression. + def exitPrimaryExpression(self, ctx: OpenSCENARIO2Parser.PrimaryExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#castExpression. + def enterCastExpression(self, ctx: OpenSCENARIO2Parser.CastExpressionContext): + raise OSC2ParsingError(msg=f"cast expression not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # object = ctx.postfixExp().getText() + # target_type = ctx.typeDeclarator().getText() + # node = CastExpression(object, target_type) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#castExpression. + def exitCastExpression(self, ctx: OpenSCENARIO2Parser.CastExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#functionApplicationExpression. + def enterFunctionApplicationExpression(self, ctx: OpenSCENARIO2Parser.FunctionApplicationExpressionContext): + self.__node_stack.append(self.__cur_node) + func_name = ctx.postfixExp().getText() + + node = FunctionApplicationExpression(func_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#functionApplicationExpression. + def exitFunctionApplicationExpression(self, ctx: OpenSCENARIO2Parser.FunctionApplicationExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#fieldAccessExpression. + def enterFieldAccessExpression(self, ctx: OpenSCENARIO2Parser.FieldAccessExpressionContext): + self.__node_stack.append(self.__cur_node) + field_name = ctx.postfixExp().getText() + "." + ctx.fieldName().getText() + + node = FieldAccessExpression(field_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#fieldAccessExpression. + def exitFieldAccessExpression(self, ctx: OpenSCENARIO2Parser.FieldAccessExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#elementAccessExpression. + def enterElementAccessExpression(self, ctx: OpenSCENARIO2Parser.ElementAccessExpressionContext): + raise OSC2ParsingError(msg=f"element access expression not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # list_name = ctx.postfixExp().getText() + # index = ctx.expression().getText() + # node = ElementAccessExpression(list_name, index) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#elementAccessExpression. + def exitElementAccessExpression(self, ctx: OpenSCENARIO2Parser.ElementAccessExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#typeTestExpression. + def enterTypeTestExpression(self, ctx: OpenSCENARIO2Parser.TypeTestExpressionContext): + raise OSC2ParsingError(msg=f"type test expression not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # object = ctx.postfixExp().getText() + # target_type = ctx.typeDeclarator().getText() + # node = TypeTestExpression(object, target_type) + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#typeTestExpression. + def exitTypeTestExpression(self, ctx: OpenSCENARIO2Parser.TypeTestExpressionContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#fieldAccess. + def enterFieldAccess(self, ctx: OpenSCENARIO2Parser.FieldAccessContext): + self.__node_stack.append(self.__cur_node) + field_name = ctx.postfixExp().getText() + "." + ctx.fieldName().getText() + node = FieldAccessExpression(field_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#fieldAccess. + def exitFieldAccess(self, ctx: OpenSCENARIO2Parser.FieldAccessContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#primaryExp. + def enterPrimaryExp(self, ctx: OpenSCENARIO2Parser.PrimaryExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#primaryExp. + def exitPrimaryExp(self, ctx: OpenSCENARIO2Parser.PrimaryExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#valueExp. + def enterValueExp(self, ctx: OpenSCENARIO2Parser.ValueExpContext): + self.__node_stack.append(self.__cur_node) + value = None + node = None + if ctx.FloatLiteral(): + value = ctx.FloatLiteral().getText() + node = FloatLiteral(value) + elif ctx.BoolLiteral(): + value = ctx.BoolLiteral().getText() + node = BoolLiteral(value) + elif ctx.StringLiteral(): + value = ctx.StringLiteral().getText() + value = value.strip('"') + node = StringLiteral(value) + + if node is not None: + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#valueExp. + def exitValueExp(self, ctx: OpenSCENARIO2Parser.ValueExpContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#listConstructor. + def enterListConstructor(self, ctx: OpenSCENARIO2Parser.ListConstructorContext): + self.__node_stack.append(self.__cur_node) + node = ListExpression() + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#listConstructor. + def exitListConstructor(self, ctx: OpenSCENARIO2Parser.ListConstructorContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#rangeConstructor. + def enterRangeConstructor(self, ctx: OpenSCENARIO2Parser.RangeConstructorContext): + raise OSC2ParsingError(msg=f"range constructor not yet supported", context=ctx) + # self.__node_stack.append(self.__cur_node) + # node = RangeExpression() + # node.set_ctx(ctx, self.current_file) + + # self.__cur_node.set_children(node) + # self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#rangeConstructor. + def exitRangeConstructor(self, ctx: OpenSCENARIO2Parser.RangeConstructorContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#identifierReference. + def enterIdentifierReference(self, ctx: OpenSCENARIO2Parser.IdentifierReferenceContext): + self.__node_stack.append(self.__cur_node) + + field_name = [] + for fn in ctx.fieldName(): + name = fn.Identifier().getText() + + field_name.append(name) + + id_name = ".".join(field_name) # TODO do not merge to single string + + node = IdentifierReference(id_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#identifierReference. + def exitIdentifierReference(self, ctx: OpenSCENARIO2Parser.IdentifierReferenceContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#argumentListSpecification. + def enterArgumentListSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentListSpecificationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#argumentListSpecification. + def exitArgumentListSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentListSpecificationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#argumentSpecification. + def enterArgumentSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentSpecificationContext): + self.__node_stack.append(self.__cur_node) + argument_name = ctx.argumentName().getText() + argument_type = ctx.typeDeclarator().getText() + default_value = None + if ctx.defaultValue(): + default_value = ctx.defaultValue().getText() + + node = Argument(argument_name, argument_type, default_value) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#argumentSpecification. + def exitArgumentSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentSpecificationContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#argumentName. + def enterArgumentName(self, ctx: OpenSCENARIO2Parser.ArgumentNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#argumentName. + def exitArgumentName(self, ctx: OpenSCENARIO2Parser.ArgumentNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#argumentList. + def enterArgumentList(self, ctx: OpenSCENARIO2Parser.ArgumentListContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#argumentList. + def exitArgumentList(self, ctx: OpenSCENARIO2Parser.ArgumentListContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#positionalArgument. + def enterPositionalArgument(self, ctx: OpenSCENARIO2Parser.PositionalArgumentContext): + self.__node_stack.append(self.__cur_node) + + node = PositionalArgument() + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#positionalArgument. + def exitPositionalArgument(self, ctx: OpenSCENARIO2Parser.PositionalArgumentContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#namedArgument. + def enterNamedArgument(self, ctx: OpenSCENARIO2Parser.NamedArgumentContext): + self.__node_stack.append(self.__cur_node) + argument_name = ctx.argumentName().getText() + + node = NamedArgument(argument_name) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#namedArgument. + def exitNamedArgument(self, ctx: OpenSCENARIO2Parser.NamedArgumentContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#physicalLiteral. + def enterPhysicalLiteral(self, ctx: OpenSCENARIO2Parser.PhysicalLiteralContext): + self.__node_stack.append(self.__cur_node) + unit_name = ctx.unitName().getText() + value = None + if ctx.FloatLiteral(): + value = ctx.FloatLiteral().getText() + else: + value = ctx.integerLiteral().getText() + + node = PhysicalLiteral(unit_name, value) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + if ctx.FloatLiteral(): + self.__node_stack.append(self.__cur_node) + float_value = ctx.FloatLiteral().getText() + value_node = FloatLiteral(float_value) + value_node.set_ctx(ctx, self.current_file) + + node.set_children(value_node) + self.__cur_node = self.__node_stack.pop() + + # Exit a parse tree produced by OpenSCENARIO2Parser#physicalLiteral. + def exitPhysicalLiteral(self, ctx: OpenSCENARIO2Parser.PhysicalLiteralContext): + self.__cur_node = self.__node_stack.pop() + + # Enter a parse tree produced by OpenSCENARIO2Parser#integerLiteral. + def enterIntegerLiteral(self, ctx: OpenSCENARIO2Parser.IntegerLiteralContext): + self.__node_stack.append(self.__cur_node) + value = None + type_def = "uint" + if ctx.UintLiteral(): + value = int(ctx.UintLiteral().getText()) + type_def = "uint" + elif ctx.HexUintLiteral(): + value = int(ctx.HexUintLiteral().getText(), 16) + type_def = "uint" + elif ctx.IntLiteral(): + value = int(ctx.IntLiteral().getText()) + type_def = "int" + else: # only the above three types of integer literal + raise ValueError("invalid literal") + + node = IntegerLiteral(type_def, value) + node.set_ctx(ctx, self.current_file) + + self.__cur_node.set_children(node) + self.__cur_node = node + + # Exit a parse tree produced by OpenSCENARIO2Parser#integerLiteral. + def exitIntegerLiteral(self, ctx: OpenSCENARIO2Parser.IntegerLiteralContext): + self.__cur_node = self.__node_stack.pop() + + +del OpenSCENARIO2Parser diff --git a/scenario_execution_base/scenario_execution_base/model/model_file_loader.py b/scenario_execution_base/scenario_execution_base/model/model_file_loader.py new file mode 100644 index 00000000..0b99701c --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/model_file_loader.py @@ -0,0 +1,52 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +import yaml +from scenario_execution_base.model.types import print_tree, deserialize +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.model.model_resolver import resolve_internal_model + + +class ModelFileLoader(object): + + def __init__(self, logger) -> None: + self.logger = logger + + def process_file(self, file_name, log_tree: bool = False, debug: bool = False): + model = self.load_file(file_name, log_tree) + + success = resolve_internal_model(model, self.logger, log_tree) + if not success: + return None + + scenarios = create_py_tree(model, self.logger) + + return scenarios + + def load_file(self, file_name, log_tree): + try: + with open(file_name, 'rb') as input_file: + serialize_data = yaml.safe_load(input_file) # nosec B301 TODO + model = deserialize(serialize_data) + except Exception as e: # pylint: disable=broad-except + self.logger.error(f"Error while loading model from {file_name}: {e}") + return None + + if log_tree: + self.logger.info("----Internal model-----") + print_tree(model, self.logger) + return model diff --git a/scenario_execution_base/scenario_execution_base/model/model_resolver.py b/scenario_execution_base/scenario_execution_base/model/model_resolver.py new file mode 100644 index 00000000..ad6e5b7d --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/model_resolver.py @@ -0,0 +1,284 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from scenario_execution_base.model.types import ActionDeclaration, ActionInherits, EnumDeclaration, EnumValueReference, KeepConstraintDeclaration, EmitDirective, Type + +from .types import UnitDeclaration, EnumValueReference, StructInherits, ActionDeclaration, ActionInherits, ActorInherits, FieldAccessExpression, BehaviorInvocation, EmitDirective, GlobalParameterDeclaration, IdentifierReference, Parameter, ModelElement, StructuredDeclaration, KeepConstraintDeclaration, NamedArgument, ParameterDeclaration, PhysicalLiteral, PositionalArgument, RelationExpression, ScenarioInherits, SIUnitSpecifier, Type, EnumMemberDeclaration, ListExpression, print_tree + +from .model_base_visitor import ModelBaseVisitor +from scenario_execution_base.model.error import OSC2ParsingError + + +def resolve_internal_model(model, logger, log_tree): + osc2scenario_resolver = ModelResolver(logger) + try: + osc2scenario_resolver.visit(model) + except OSC2ParsingError as e: + logger.error( + f'Error while creating tree:\nTraceback in "{e.filename}":\n -> {e.context}\n' + f'{e.__class__.__name__}: {e.msg}' + ) + return False + + if log_tree: + logger.info("----Internal model (resolved)-----") + print_tree(model, logger) + return True + + +class ModelResolver(ModelBaseVisitor): + + def __init__(self, logger) -> None: + super().__init__() + self.logger = logger + + def visit_physical_literal(self, node: PhysicalLiteral): + unit = node.resolve(node.unit) + if unit is None: + raise OSC2ParsingError( + msg=f'Physical unit "{node.unit}" not defined.', context=node.get_ctx()) + node.unit = unit + si_unit_specifier = node.unit.find_first_child_of_type(SIUnitSpecifier) + if si_unit_specifier is None: + raise OSC2ParsingError( + msg=f'SIUnitSpecifier of physical unit "{node.unit.name}" not defined.', context=node.get_ctx()) + + def visit_identifier_reference(self, node: IdentifierReference): + resolved = node.resolve(node.ref) + if resolved is None: + raise OSC2ParsingError( + msg=f'Identifier "{node.ref}" not defined.', context=node.get_ctx()) + node.ref = resolved + + def visit_type(self, node: Type): + type_string = node.type_def + if node.is_list: + if not type_string.startswith('listof'): + raise OSC2ParsingError( + msg=f'Invalid list type "{type_string}".', context=node.get_ctx()) + type_string = type_string.removeprefix('listof') + + if type_string not in ['string', 'int', 'bool', 'float', 'uint']: + resolved = node.resolve(type_string) + if resolved is None: + raise OSC2ParsingError( + msg=f'Type "{type_string}" not defined.', context=node.get_ctx()) + node.type_def = resolved + + def visit_unit_declaration(self, node: UnitDeclaration): + resolved = node.resolve(node.physical_name) + if resolved is None: + raise OSC2ParsingError( + msg=f'Unit declaration refers to unknown physical name "{node.physical_name}".', context=node.get_ctx()) + node.physical_name = resolved + + def visit_keep_constraint_declaration(self, node: KeepConstraintDeclaration): + self.visit_children(node) + if node.get_child_count() == 1 and isinstance(node.get_child(0), RelationExpression) and node.get_child(0).get_child_count() == 2: + if node.get_child(0).operator != "==": + raise OSC2ParsingError( + msg=f'Only relation "==" is currently supported in "keep".', context=node.get_ctx()) + field_exp = node.get_child(0).find_first_child_of_type(FieldAccessExpression) + + if not field_exp: + raise OSC2ParsingError( + msg=f'FieldAccessExpression not found.', context=node.get_ctx()) + if not field_exp.field_name.startswith('it.'): + raise OSC2ParsingError( + msg=f'FieldAccessExpression only supports "it." prefix, not "{field_exp.field_name}".', context=node.get_ctx()) + + definition, _ = node.get_parent().get_type() + if not isinstance(definition, StructuredDeclaration): + raise OSC2ParsingError( + msg=f'keep expected reference to structured type.', context=node.get_ctx()) + + parameters = definition.get_resolved_value() + expected_member_name = field_exp.field_name.removeprefix("it.") + + member_path = expected_member_name.split('.') + current_params = parameters + for current in member_path: + if current not in current_params: + raise OSC2ParsingError( + msg=f'keep reference {field_exp.field_name} not found in {definition.name}. Unknown key "{current}"', context=node.get_ctx()) + current_params = current_params[current] + else: + raise OSC2ParsingError( + msg=f'Keep uses unsupported expression: allowed "==" only.', context=node.get_ctx()) + + def check_parameter_type(self, node: Parameter): + val = node.get_value_child() + if isinstance(val, KeepConstraintDeclaration): + pass + else: + if val is not None: + val_type = val.get_type_string() + param_type = node.field_type + # if isinstance(node.field_type, Type) and node.field_type.is_list: + # param_type = "listof" + param_type + if param_type == 'uint': + param_type = 'int' + if val_type != param_type: + raise OSC2ParsingError( + msg=f'Parameter type "{param_type}" does not match value type "{val_type}".', context=node.get_ctx()) + + # check list entries + if isinstance(val, ListExpression): + expected_type = param_type.removeprefix('listof') + for child in val.get_children(): + member_type = child.get_type_string() + if expected_type != member_type: + raise OSC2ParsingError( + msg=f'List entry does not have valid type. Expected "{expected_type}", found "{member_type}".', context=node.get_ctx()) + + def visit_global_parameter_declaration(self, node: GlobalParameterDeclaration): + self.visit_children(node) + self.check_parameter_type(node) + + def visit_parameter_declaration(self, node: ParameterDeclaration): + self.visit_children(node) + self.check_parameter_type(node) + + def visit_actor_inherits(self, node: ActorInherits): + resolved = node.resolve(node.actor) + if resolved is None: + raise OSC2ParsingError( + msg=f'Actor inherits from unknown "{node.actor}".', context=node.get_ctx()) + node.actor = resolved + + def visit_struct_inherits(self, node: StructInherits): + resolved = node.resolve(node.struct_name) + if resolved is None: + raise OSC2ParsingError( + msg=f'Struct inherits from unknown "{node.struct_name}".', context=node.get_ctx()) + node.struct_name = resolved + + def visit_action_inherits(self, node: ActionInherits): + resolved = node.resolve(node.qualified_behavior_name) + if resolved is None: + raise OSC2ParsingError( + msg=f'Action inherits from unknown "{node.qualified_behavior_name}".', context=node.get_ctx()) + node.qualified_behavior_name = resolved + + def visit_scenario_inherits(self, node: ScenarioInherits): + resolved = node.resolve(node.qualified_behavior_name) + if resolved is None: + raise OSC2ParsingError( + msg=f'Scenario inherits from unknown "{node.qualified_behavior_name}".', context=node.get_ctx()) + node.qualified_behavior_name = resolved + + def visit_behavior_invocation(self, node: BehaviorInvocation): + qualified_behavior_name = node.behavior + if node.actor: + resolved_actor = node.resolve(node.actor) + if resolved_actor is None: + raise OSC2ParsingError( + msg=f'BehaviorInvocation refers to unknown actor "{node.actor}".', context=node.get_ctx()) + node.actor = resolved_actor + + current, _ = node.actor.get_type() + resolved = None + while current and resolved is None: # look for action in all base_types + qualified_behavior_name = current.name + "." + node.behavior + resolved = node.resolve(qualified_behavior_name) + current = current.get_base_type() + else: + resolved = node.resolve(node.behavior) + + if not resolved: + raise OSC2ParsingError( + msg=f'BehaviorInvocation uses unknown behavior "{qualified_behavior_name}".', context=node.get_ctx()) + node.behavior = resolved + + pos_arg_count = 0 + named = False + param_keys = node.get_parameter_names() + max_named_arg_count = len(param_keys) + for child in node.get_children(): + if isinstance(child, NamedArgument): + named = True + if child.argument_name not in param_keys: + raise OSC2ParsingError( + msg=f'Named argument {child.argument_name} unknown.', context=node.get_ctx()) + + elif isinstance(child, PositionalArgument): + if named: + raise OSC2ParsingError( + msg=f'No positional argument allowed after named.', context=node.get_ctx()) + if pos_arg_count >= max_named_arg_count: + raise OSC2ParsingError( + msg=f'Too many positional arguments.', context=node.get_ctx()) + pos_arg_count += 1 + + super().visit_behavior_invocation(node) + + def visit_action_declaration(self, node: ActionDeclaration): + super().visit_action_declaration(node) + + param_names = node.get_parameter_names() + if 'name' in param_names: + raise OSC2ParsingError( + msg=f'ActionDeclaration {node.name} uses reserved paramater name "name".', context=node.get_ctx()) + if 'associated_actor' in param_names: + raise OSC2ParsingError( + msg=f'ActionDeclaration {node.name} uses reserved paramater name "associated_actor".', context=node.get_ctx()) + + actor_name = None + if "." in node.qualified_behavior_name: + actor_name = node.qualified_behavior_name.split('.')[0] + if actor_name: + resolved_actor = node.resolve(actor_name) + if resolved_actor is None: + raise OSC2ParsingError( + msg=f'ActionDeclaration {node.name} refers to unknown actor "{actor_name}".', context=node.get_ctx()) + node.actor = resolved_actor + + def visit_enum_value_reference(self, node: EnumValueReference): + # skip parameter level (to allow parameter names to be similar to enum-type-names) + enum_type = node.get_parent().resolve(node.enum_name) + if enum_type is None: + raise OSC2ParsingError( + msg=f'Enum type {node.enum_name} unknown.', context=node.get_ctx()) + node.enum_name = enum_type + + member = None + for child in enum_type.get_children(): + if isinstance(child, ModelElement) and child.member_name == node.enum_member_name: + member = child + break + if member is None: + raise OSC2ParsingError( + msg=f'Enum type {node.enum_name} does not have a member "{node.enum_member_name}".', context=node.get_ctx()) + node.enum_member_name = member + + def visit_emit_directive(self, node: EmitDirective): + if node.event_name not in ['start', 'end', 'fail']: + node.event = node.resolve(node.event_name) + if node.event is None: + raise OSC2ParsingError( + msg=f'EmitDirective refers to unknown event {node.event_name}.', context=node.get_ctx()) + + def visit_enum_declaration(self, node: EnumDeclaration): + next_numeric_val = 0 + for child in node.get_children(): + if isinstance(child, EnumMemberDeclaration): + if child.numeric_value is None: + child.numeric_value = next_numeric_val + next_numeric_val += 1 + else: + next_numeric_val = child.numeric_value + 1 + + return super().visit_enum_declaration(node) diff --git a/scenario_execution_base/scenario_execution_base/model/model_to_py_tree.py b/scenario_execution_base/scenario_execution_base/model/model_to_py_tree.py new file mode 100644 index 00000000..97ce980e --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/model_to_py_tree.py @@ -0,0 +1,282 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import py_trees +from py_trees.common import Access, Status +from pkg_resources import iter_entry_points + +import inspect + +from scenario_execution_base.model.types import EventReference, CoverDeclaration, ScenarioDeclaration, DoMember, WaitDirective, EmitDirective, BehaviorInvocation, EventCondition, EventDeclaration, RelationExpression, LogicalExpression, ElapsedExpression, PhysicalLiteral +from scenario_execution_base.model.model_base_visitor import ModelBaseVisitor +from scenario_execution_base.model.error import OSC2ParsingError + + +def create_py_tree(model, logger): + model_to_py_tree = ModelToPyTree(logger) + scenario = None + try: + scenario = model_to_py_tree.build(model) + except OSC2ParsingError as e: + logger.error( + f'Error while creating tree:\nTraceback in "{e.filename}":\n -> {e.context}\n' + f'{e.__class__.__name__}: {e.msg}' + ) + return None + return [scenario] + + +class TopicEquals(py_trees.behaviour.Behaviour): + """ + Class to listen to a topic in Blackboard and check if it equals the defined message + + Args: + key [str]: topic to listen to + msg [str]: target message to match + namespace [str]: namespace of the key + """ + + def __init__(self, key: str, msg: str, namespace: str = None): + super().__init__(self.__class__.__name__) + + self.namespace = namespace + self.key = key + self.msg = msg + + self.client = self.attach_blackboard_client(namespace=self.namespace) + self.client.register_key(self.key, access=Access.READ) + + def update(self): + """ + Check the message on the topic equals the target message + """ + msg_on_blackboard = self.client.get(self.key) + if msg_on_blackboard == self.msg: + return Status.SUCCESS + return Status.RUNNING + + +class TopicPublish(py_trees.behaviour.Behaviour): + """ + Class to publish a message to a topic + + Args: + key [str]: topic to publish on + msg [str]: message to publish on that topic + namespace [str]: namespace of the key + """ + + def __init__(self, name: "TopicPublish", key: str, msg: str, namespace: str = None): + super().__init__(name) + + self.namespace = namespace + self.key = key + self.msg = msg + + self.client = self.attach_blackboard_client(namespace=self.namespace) + self.client.register_key(self.key, access=Access.WRITE) + + def setup(self, **kwargs): + """ + Setup empty topic on blackboard + + This is to prevent the "Reader" from reading before the topic exists. + """ + self.client.set(self.key, '') + + def update(self): + """ + publish the message to topic + """ + self.client.set(self.key, self.msg) + return Status.SUCCESS + + +class ModelToPyTree(object): + + def __init__(self, logger): + self.logger = logger + + def build(self, tree): + if tree.find_children_of_type(CoverDeclaration): + raise ValueError("Model still contains CoverageDeclarations.") + behavior_builder = self.BehaviorInit(self.logger) + behavior_builder.visit(tree) + + behavior_tree = behavior_builder.get_behavior_tree() + + if behavior_tree: + print(py_trees.display.ascii_tree(behavior_tree)) + + return behavior_tree + + class BehaviorInit(ModelBaseVisitor): + def __init__(self, logger) -> None: + super().__init__() + self.logger = logger + self.root_behavior = None + self.__cur_behavior = None + + def get_behavior_tree(self): + return self.root_behavior + + def visit_scenario_declaration(self, node: ScenarioDeclaration): + scenario_name = node.qualified_behavior_name + if self.root_behavior: + raise ValueError( + f"Could not add scenario {scenario_name}. Scenario {self.root_behavior.name} already defined.") + + self.root_behavior = py_trees.composites.Sequence(name=scenario_name) + self.__cur_behavior = self.root_behavior + + super().visit_scenario_declaration(node) + + def visit_do_member(self, node: DoMember): + composition_operator = node.composition_operator + if composition_operator == "serial": + behavior = py_trees.composites.Sequence(name=node.name) + elif composition_operator == "parallel": + behavior = py_trees.composites.Parallel(name=node.name, policy=py_trees.common.ParallelPolicy.SuccessOnAll()) + elif composition_operator == "one_of": + behavior = py_trees.composites.Parallel(name=node.name, policy=py_trees.common.ParallelPolicy.SuccessOnOne()) + else: + raise NotImplementedError(f"scenario operator {composition_operator} not yet supported.") + + parent = self.__cur_behavior + self.__cur_behavior.add_child(behavior) + self.__cur_behavior = behavior + self.visit_children(node) + self.__cur_behavior = parent + + def visit_wait_directive(self, node: WaitDirective): + child = node.get_only_child() + + if isinstance(child, (EventCondition, EventReference)): + behavior = self.visit(child) + + self.__cur_behavior.add_child(behavior) + else: + raise OSC2ParsingError(msg="Invalid wait directive.", context=node.get_ctx()) + + def visit_emit_directive(self, node: EmitDirective): + if node.event_name in ['start', 'end', 'fail']: + self.__cur_behavior.add_child(TopicPublish( + name=f"emit {node.event_name}", key=f"/{self.root_behavior.name}/{node.event_name}", msg=True)) + else: + qualified_name = node.event.get_qualified_name() + self.__cur_behavior.add_child(TopicPublish( + name=f"emit {node.event_name}", key=qualified_name, msg=True)) + + def visit_behavior_invocation(self, node: BehaviorInvocation): + behavior_name = node.behavior.name + + final_args = node.get_resolved_value() + + available_plugins = [] + for entry_point in iter_entry_points(group='scenario_execution.actions', name=None): + # self.logger.debug(f'entry_point.name is {entry_point.name}') + if entry_point.name == behavior_name: + available_plugins.append(entry_point) + if not available_plugins: + raise OSC2ParsingError( + msg=f'No plugins found for action "{behavior_name}".', + context=node.get_ctx() + ) + if len(available_plugins) > 1: + self.logger.error(f'More than one plugin is found for "{behavior_name}".') + for available_plugin in available_plugins: + self.logger.error( + f'Found available plugin for "{behavior_name}" ' + f'in module "{available_plugin.module_name}".') + raise OSC2ParsingError( + msg=f'More than one plugin is found for "{behavior_name}".', + context=node.get_ctx() + ) + behavior_cls = available_plugins[0].load() + + if not issubclass(behavior_cls, py_trees.behaviour.Behaviour): + raise OSC2ParsingError( + msg=f"Found plugin for '{behavior_name}', but it's not derived from py_trees.behaviour.Behaviour.", + context=node.get_ctx() + ) + + plugin_args = inspect.getfullargspec(behavior_cls.__init__).args + plugin_args.remove("self") + + final_args["name"] = node.name + + if node.actor: + final_args["associated_actor"] = node.actor.get_resolved_value() + final_args["associated_actor"]["name"] = node.actor.name + + missing_args = [] + for element in plugin_args: + if element not in final_args: + missing_args.append(element) + if missing_args: + raise OSC2ParsingError( + msg=f'Plugin {behavior_name} requires arguments that are not defined in osc. Missing: {missing_args}', context=node.get_ctx() + ) + log_name = None + if final_args["name"]: + log_name = final_args["name"] + else: + log_name = type(node).__name__ + self.logger.info( + f"Instantiate action '{log_name}', plugin '{behavior_name}' with:\nArguments: {final_args}") + try: + instance = behavior_cls(**final_args) + except Exception as e: + raise OSC2ParsingError(msg=f'Error while initializing plugin {behavior_name}: {e}', context=node.get_ctx()) from e + self.__cur_behavior.add_child(instance) + + def visit_event_reference(self, node: EventReference): + event = node.resolve(node.event_path) + name = event.get_qualified_name() + return TopicEquals(key=name, msg=True) + + 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() + 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)) + else: + raise OSC2ParsingError( + msg=f'Invalid event condition {child}', context=node.get_ctx()) + return expression + + def visit_elapsed_expression(self, node: ElapsedExpression): + child = node.find_first_child_of_type(PhysicalLiteral) + if child is None: + raise OSC2ParsingError( + msg=f'Elapsed expression currently only supports PhysicalLiteral.', context=node.get_ctx()) + return child.get_resolved_value() + + def visit_event_declaration(self, node: EventDeclaration): + if node.name in ['start', 'end', 'fail']: + raise OSC2ParsingError( + msg=f'EventDeclaration uses reserved name {node.name}.', context=node.get_ctx() + ) + else: + qualified_name = node.get_qualified_name() + client = self.__cur_behavior.attach_blackboard_client() + client.register_key(qualified_name, access=Access.WRITE) diff --git a/scenario_execution_base/scenario_execution_base/model/osc2_parser.py b/scenario_execution_base/scenario_execution_base/model/osc2_parser.py new file mode 100644 index 00000000..b05f38da --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/osc2_parser.py @@ -0,0 +1,131 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import sys +import re + +from antlr4 import FileStream, CommonTokenStream +from antlr4.error.ErrorListener import ErrorListener +from antlr4.tree.Tree import TerminalNodeImpl, ParseTreeWalker +from scenario_execution_base.osc2_parsing.OpenSCENARIO2Parser import OpenSCENARIO2Parser +from scenario_execution_base.osc2_parsing.OpenSCENARIO2Lexer import OpenSCENARIO2Lexer +from scenario_execution_base.model.error import OSC2ParsingError +from scenario_execution_base.model.model_builder import ModelBuilder +from scenario_execution_base.model.types import print_tree +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.model.model_resolver import resolve_internal_model + + +class OpenScenario2Parser(object): + """ Helper class for parsing openscenario 2 files """ + + def __init__(self, logger) -> None: + self.logger = logger + + def process_file(self, file, log_model: bool = False, debug: bool = False): + """ Convenience method to execute the parsing and print out tree """ + + parsed_tree, errors = self.parse_file(file, log_model) + if errors: + return None + + model = self.create_internal_model(parsed_tree, file, log_model, debug) + if model is None: + return None + + scenarios = create_py_tree(model, self.logger) + + return scenarios + + def load_internal_model(self, tree, file_name: str, log_model: bool = False, debug: bool = False): + model_builder = ModelBuilder(self.logger, self.parse_file, file_name, log_model) + walker = ParseTreeWalker() + + model = None + try: + walker.walk(model_builder, tree) + model = model_builder.get_model() + except OSC2ParsingError as e: + self.logger.error( + f'Error creating internal model: Traceback in "{e.filename}":\n -> {e.context}\n' + f'{e.__class__.__name__}: {e.msg}' + ) + if debug: + self.logger.info(str(e)) + return None + if log_model: + self.logger.info("----Internal model-----") + print_tree(model, self.logger) + return model + + def create_internal_model(self, tree, file_name: str, log_model: bool = False, debug: bool = False): + model = self.load_internal_model(tree, file_name, log_model, debug) + if model is None: + return None + + ret = resolve_internal_model(model, self.logger, log_model) + if ret: + return model + + return None + + def parse_file(self, file: str, log_model: bool = False, error_prefix=""): + """ Execute the parsing """ + try: + input_stream = FileStream(file) + except (OSError, UnicodeDecodeError) as e: + self.logger.error(f'{e}') + sys.exit(1) + return self.parse_input_stream(input_stream, log_model, error_prefix) + + def parse_input_stream(self, input_stream, log_model=False, error_prefix=""): + """ Execute the parsing """ + lexer = OpenSCENARIO2Lexer(input_stream) + stream = CommonTokenStream(lexer) + + parser = OpenSCENARIO2Parser(stream) + # if quiet: + parser.removeErrorListeners() + + class TestErrorListener(ErrorListener): + def __init__(self, prefix: str) -> None: + self.prefix = prefix + super().__init__() + + def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): # pylint: disable=invalid-name + print(self.prefix + "line " + str(line) + ":" + + str(column) + " " + msg, file=sys.stderr) + + parser.addErrorListener(TestErrorListener(error_prefix)) + tree = parser.osc_file() + errors = parser.getNumberOfSyntaxErrors() # pylint: disable=no-member + if log_model: + self.print_parsed_osc_tree(tree, self.logger, parser.ruleNames) + + del parser + return tree, errors + + @staticmethod + def print_parsed_osc_tree(tree, logger, rule_names, indent=0): + """ Print the parsed tree for debugging purposes """ + if isinstance(tree, TerminalNodeImpl): + if not re.match(r"\r?\n[ \t]*", tree.getText()): + logger.info("{0}TOKEN '{1}'".format(" " * indent, tree.getText())) + else: + logger.info("{0}{1}".format(" " * indent, rule_names[tree.getRuleIndex()])) + if tree.children: + for child in tree.children: + OpenScenario2Parser.print_parsed_osc_tree(child, logger, rule_names, indent+1) diff --git a/scenario_execution_base/scenario_execution_base/model/types.py b/scenario_execution_base/scenario_execution_base/model/types.py new file mode 100644 index 00000000..c71b3f04 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/model/types.py @@ -0,0 +1,2072 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from typing import List +from scenario_execution_base.model.error import OSC2ParsingError +import sys + + +def print_tree(elem, logger, whitespace=""): + + children = "" + for child in elem.get_children(): + if not isinstance(child, ModelElement): + children += f"{child} " + + logger.info(f"{whitespace}{elem}{children}") + + for child in elem.get_children(): + if isinstance(child, ModelElement): + print_tree(child, logger, whitespace + " ") + + +def serialize(elem): + result = {} + children = elem.get_children() + if children: + result[type(elem).__name__] = {} + args = elem.__init__.__code__.co_varnames[1:] + for arg in args: + try: + result[type(elem).__name__][arg] = elem.__dict__[arg] + except KeyError as e: + raise KeyError(f"Invalid key for element {type(elem).__name__}: {e}") from e + result[type(elem).__name__]['_children'] = [] + for child in children: + result[type(elem).__name__]['_children'].append(serialize(child)) + else: + result[type(elem).__name__] = {} + return result + + +def deserialize(elem): + types_module = sys.modules[__name__] + result = getattr(types_module, 'CompilationUnit')() + for current in elem: + for key, value in current.items(): + elem_attributes = {k: v for k, v in value.items() if k != '_children'} + module = getattr(types_module, key) + inst = module(**elem_attributes) + if '_children' in value: + children = deserialize(value['_children']) + for child_node in children.get_children(): + inst.set_children(child_node) + result.set_children(inst) + return result + + +class ModelElement(object): + def __init__(self, name=""): + self.__context = None # For error logging only + self.__line = None + self.__column = None + self.__filename = None + self.__children = [] # child node + self.__parent = None + self.name = name + self.iter = None + + def get_value_child(self): + return None + # raise NotImplementedError() + + def get_child_count(self): + return len(self.__children) + + def get_children(self): + if self.__children is not None: + for child in self.__children: + yield child + + def get_child(self, i): + return self.__children[i] + + def get_only_child(self): + if self.get_child_count() != 1: + raise ValueError(f"Expected only one child. Found {self.get_child_count()}") + return self.get_child(0) + + def set_children(self, *childs): + for child in childs: + if child is not None: + if isinstance(child, List): + for ch in child: + if isinstance(ch, ModelElement): + ch.__parent = self # pylint: disable=protected-access,unused-private-member + self.__children.append(ch) + else: + if isinstance(child, ModelElement): + child.__parent = self # pylint: disable=protected-access,unused-private-member + self.__children.append(child) + + def find_children_of_type(self, typename): + children_of_type = [] + for child in self.__children: + if isinstance(child, typename): + children_of_type.append(child) + elif isinstance(child, ModelElement): + children_of_type.extend(child.find_children_of_type(typename)) + return children_of_type + + def find_first_child_of_type(self, typename, unique=True): + found = None + for child in self.__children: + if isinstance(child, typename): + if unique and found is not None: + raise ValueError(f"Child of type {typename} not unique.") + found = child + return found + + def get_child_with_expected_type(self, pos, typename): + child = self.get_child(pos) + if not isinstance(child, typename): + raise OSC2ParsingError( + msg=f'Child at pos {pos} is expected to be of type {typename.__name__}, but is {type(child).__name__}.', context=child.get_ctx()) + return child + + def find_parent(self, typename): + if self.get_parent() is not None: + child = self.get_parent().find_first_child_of_type(typename) + if child: + return child + else: + return self.get_parent().find_parent(typename) + return None + + def find_reference_by_name(self, name, visited): + + for child in self.__children: + if isinstance(child, ModelElement) and child not in visited: + visited.append(child) + if child.name == name: + return child + + if self.get_parent(): + found = self.get_parent().find_reference_by_name(name, visited) + if found: + return found + return None + + def resolve(self, name): + visited = [self] + if self.get_parent() is not None: + return self.get_parent().find_reference_by_name(name, visited) + return None + + def get_parent(self): + return self.__parent + + def delete_child(self, child): + self.__children.remove(child) + + def set_loc(self, line, column): + self.__line = line + self.__column = column + + def set_ctx(self, ctx, filename: str): + self.__line = ctx.start.line + self.__column = ctx.start.column + self.__context = ctx.getText() + self.__filename = filename + + def get_ctx(self): + return self.__line, self.__column, self.__context, self.__filename + + def accept(self, visitor): + pass + + def enter_node(self, listener): + pass + + def exit_node(self, listener): + pass + + def __iter__(self): + self.iter = iter(self.__children) + return self.iter + + def __next__(self): + return next(self.iter) + + def __str__(self) -> str: + output = f"{self.__class__.__name__}({self.name})" + first = True + for attr in vars(self): + if not attr.startswith('_') and attr != "name" and attr != "iter": + if first: + output += ": " + first = False + else: + output += ", " + output += attr + + attr_val = getattr(self, attr) + if isinstance(attr_val, ModelElement): + output += "=" + f"{attr_val.__class__.__name__}({attr_val.name})" + else: + output += "=" + str(attr_val) + return output + + +class CompilationUnit(ModelElement): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_compilation_unit"): + listener.enter_compilation_unit(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_compilation_unit"): + listener.exit_compilation_unit(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_compilation_unit"): + return visitor.visit_compilation_unit(self) + else: + return visitor.visit_children(self) + + +def merge_nested_dicts(dict1, dict2, key_must_exist=True): + for key, value in dict2.items(): + if key_must_exist and key not in dict1: + raise ValueError(f"Key not found {key}") + if key in dict1 and isinstance(dict1[key], dict) and isinstance(value, dict): + merge_nested_dicts(dict1[key], value, key_must_exist) + else: + dict1[key] = value + + +class Declaration(ModelElement): + + def __init__(self, name=""): + super().__init__(name) + self.base_type = None + self.values = {} + + def get_declaration_string(self): + return f"base_type: {self.base_type}, values: {self.values}" + + def get_qualified_name(self): + res = "" + current = self + while current: + if current.name: + res = "/" + current.name + res + current = current.get_parent() + return res + + def get_base_type(self): + return None + + def get_resolved_value(self): + return None + + def get_type_string(self): + return self.name + + +class Parameter(Declaration): + + def get_value_child(self): + if self.get_child_count() != 2: + return None + + for child in self.get_children(): + if isinstance(child, (StringLiteral, FloatLiteral, BoolLiteral, IntegerLiteral, FunctionApplicationExpression, IdentifierReference, PhysicalLiteral, EnumValueReference, ListExpression)): + return child + return None + + def get_resolved_value(self): + param_type, is_list = self.get_type() + vals = {} + params = {} + if self.get_value_child(): + vals = self.get_value_child().get_resolved_value() + + if isinstance(param_type, StructuredDeclaration) and not is_list: + params = param_type.get_resolved_value() + merge_nested_dicts(params, vals) + else: + params = vals + + for child in self.get_children(): + if isinstance(child, KeepConstraintDeclaration): # for variable only? + tmp = child.get_resolved_value() + merge_nested_dicts(params, tmp) + return params + + def get_type(self): + declared_type = self.find_first_child_of_type(Type) + return declared_type.type_def, declared_type.is_list + + def get_type_string(self): + val_type, is_list = self.get_type() + + if isinstance(val_type, ModelElement): + val_type = val_type.get_type_string() + if is_list: + val_type = 'listof' + val_type + return val_type + + +class StructuredDeclaration(Declaration): + + def get_parameter_names(self): + names = [] + + if self.get_base_type(): + names = self.get_base_type().get_parameter_names() + + for child in self.get_children(): + if isinstance(child, ParameterDeclaration): + names.append(child.name) + return list(set(names)) + + def get_resolved_value(self): + params = {} + + # set values defined in base type + if self.get_base_type(): + params = self.get_base_type().get_resolved_value() + + named = False + pos = 0 + param_keys = list(params.keys()) + for child in self.get_children(): + if isinstance(child, ParameterDeclaration): + param_type, _ = child.get_type() + + # set values defined in type itself + if isinstance(param_type, ModelElement): + params[child.name] = param_type.get_resolved_value() + + # set values from parameter value + val = child.get_value_child() + if val: + if isinstance(val, KeepConstraintDeclaration): + tmp = val.get_resolved_value() + for key, val in tmp.items(): + if key not in params: + raise OSC2ParsingError( + msg=f'Keep Constraint Declaration specifies unknown member "{key}".', context=self.get_ctx()) + else: + params[key] = val + else: + params[child.name] = val.get_resolved_value() + else: + if child.name not in params: + params[child.name] = None + elif isinstance(child, PositionalArgument): + if named: + raise OSC2ParsingError( + msg=f'Positional argument after named argument not allowed.', context=child.get_ctx()) + params[param_keys[pos]] = child.get_resolved_value() + pos += 1 + elif isinstance(child, NamedArgument): + named = True + params[child.name] = child.get_resolved_value() + elif isinstance(child, KeepConstraintDeclaration): # for behaviorinvocation only? + tmp = child.get_resolved_value() + merge_nested_dicts(params, tmp, key_must_exist=False) + + return params + + def get_type(self): + return self, False + + def get_type_string(self): + return self.name + + +class Expression(ModelElement): + pass + + +class PhysicalTypeDeclaration(Declaration): + + def __init__(self, type_name): + super().__init__(type_name) + self.type_name = type_name + + def enter_node(self, listener): + if hasattr(listener, "enter_physical_type_declaration"): + listener.enter_physical_type_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_physical_type_declaration"): + listener.exit_physical_type_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_physical_type_declaration"): + return visitor.visit_physical_type_declaration(self) + else: + return visitor.visit_children(self) + + def get_resolved_value(self): + return None + + def get_type_string(self): + return self.type_name + + +class UnitDeclaration(Declaration): + + def __init__(self, unit_name, physical_name): + super().__init__(unit_name) + self.unit_name = unit_name + self.physical_name = physical_name + + def enter_node(self, listener): + if hasattr(listener, "enter_unit_declaration"): + listener.enter_unit_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_unit_declaration"): + listener.exit_unit_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_unit_declaration"): + return visitor.visit_unit_declaration(self) + else: + return visitor.visit_children(self) + + def get_type_string(self): + return self.physical_name.name + + +class SIUnitSpecifier(ModelElement): + + def __init__(self, factor, offset): + super().__init__() + self.factor = factor + self.offset = offset + + def enter_node(self, listener): + if hasattr(listener, "enter_si_unit_specifier"): + listener.enter_si_unit_specifier(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_si_unit_specifier"): + listener.exit_si_unit_specifier(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_si_unit_specifier"): + return visitor.visit_si_unit_specifier(self) + else: + return visitor.visit_children(self) + + +class SIBaseExponent(ModelElement): + + def __init__(self, unit_name): + super().__init__() + self.unit_name = unit_name + + def enter_node(self, listener): + if hasattr(listener, "enter_si_base_exponent"): + listener.enter_si_base_exponent(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_si_base_exponent"): + listener.exit_si_base_exponent(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_si_base_exponent"): + return visitor.visit_si_base_exponent(self) + else: + return visitor.visit_children(self) + + +class EnumDeclaration(Declaration): + + def __init__(self, enum_name): + super().__init__(enum_name) + self.enum_name = enum_name + + def enter_node(self, listener): + if hasattr(listener, "enter_enum_declaration"): + listener.enter_enum_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_enum_declaration"): + listener.exit_enum_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_enum_declaration"): + return visitor.visit_enum_declaration(self) + else: + return visitor.visit_children(self) + + def get_resolved_value(self): + return None + + +class EnumMemberDeclaration(Declaration): + + def __init__(self, member_name, numeric_value): + super().__init__() + self.member_name = member_name + self.numeric_value = numeric_value + + def enter_node(self, listener): + if hasattr(listener, "enter_enum_member_decl"): + listener.enter_enum_member_decl(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_enum_member_decl"): + listener.exit_enum_member_decl(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_enum_member_decl"): + return visitor.visit_enum_member_declaration(self) + else: + return visitor.visit_children(self) + + +class EnumValueReference(ModelElement): + + def __init__(self, enum_name, enum_member_name): + super().__init__() + self.enum_name = enum_name + self.enum_member_name = enum_member_name + + def enter_node(self, listener): + if hasattr(listener, "enter_enum_value_reference"): + listener.enter_enum_value_reference(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_enum_value_reference"): + listener.exit_enum_value_reference(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_enum_value_reference"): + return visitor.visit_enum_value_reference(self) + else: + return visitor.visit_children(self) + + def get_type_string(self): + return self.enum_name.name + + def get_resolved_value(self): + return (self.enum_member_name.member_name, self.enum_member_name.numeric_value) + + +class InheritsCondition(ModelElement): + + def __init__(self, field_name, bool_literal): + super().__init__() + self.field_name = field_name + + def enter_node(self, listener): + if hasattr(listener, "enter_inherits_condition"): + listener.enter_inherits_condition(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_inherits_condition"): + listener.exit_inherits_condition(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_inherits_condition"): + return visitor.visit_inherits_condition(self) + else: + return visitor.visit_children(self) + + +class StructDeclaration(StructuredDeclaration): + + def __init__(self, struct_name): + super().__init__(struct_name) + self.struct_name = struct_name + + def enter_node(self, listener): + if hasattr(listener, "enter_struct_declaration"): + listener.enter_struct_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_struct_declaration"): + listener.exit_struct_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_struct_declaration"): + return visitor.visit_struct_declaration(self) + else: + return visitor.visit_children(self) + + def get_base_type(self): + inherits = self.find_first_child_of_type(StructInherits) + if inherits: + return inherits.struct_name + return None + + +class Inheritance(ModelElement): + + def __init__(self): + super().__init__() + + +class StructInherits(Inheritance): + + def __init__(self, struct_name): + super().__init__() + self.struct_name = struct_name + + def enter_node(self, listener): + if hasattr(listener, "enter_struct_inherits"): + listener.enter_struct_inherits(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_struct_inherits"): + listener.exit_struct_inherits(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_struct_inherits"): + return visitor.visit_struct_inherits(self) + else: + return visitor.visit_children(self) + + +class ActorDeclaration(StructuredDeclaration): + + def __init__(self, actor): + super().__init__(actor) + self.actor = actor + + def enter_node(self, listener): + if hasattr(listener, "enter_actor_declaration"): + listener.enter_actor_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_actor_declaration"): + listener.exit_actor_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_actor_declaration"): + return visitor.visit_actor_declaration(self) + else: + return visitor.visit_children(self) + + def get_base_type(self): + inherits = self.find_first_child_of_type(ActorInherits) + if inherits: + return inherits.actor + return None + + +class ActorInherits(Inheritance): + + def __init__(self, actor): + super().__init__() + self.actor = actor + + def enter_node(self, listener): + if hasattr(listener, "enter_actor_inherits"): + listener.enter_actor_inherits(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_actor_inherits"): + listener.exit_actor_inherits(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_actor_inherits"): + return visitor.visit_actor_inherits(self) + else: + return visitor.visit_children(self) + + +class ScenarioDeclaration(StructuredDeclaration): + + def __init__(self, qualified_behavior_name): + super().__init__(qualified_behavior_name) + self.qualified_behavior_name = qualified_behavior_name + + def enter_node(self, listener): + if hasattr(listener, "enter_scenario_declaration"): + listener.enter_scenario_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_scenario_declaration"): + listener.exit_scenario_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_scenario_declaration"): + return visitor.visit_scenario_declaration(self) + else: + return visitor.visit_children(self) + + def get_base_type(self): + inherits = self.find_first_child_of_type(ScenarioInherits) + if inherits: + return inherits.qualified_behavior_name + return None + + +class ScenarioInherits(Inheritance): + + def __init__(self, qualified_behavior_name): + super().__init__() + self.qualified_behavior_name = qualified_behavior_name + + def enter_node(self, listener): + if hasattr(listener, "enter_scenario_inherits"): + listener.enter_scenario_inherits(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_scenario_inherits"): + listener.exit_scenario_inherits(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_scenario_inherits"): + return visitor.visit_scenario_inherits(self) + else: + return visitor.visit_children(self) + + +class ActionDeclaration(StructuredDeclaration): + + def __init__(self, qualified_behavior_name): + self.actor = None + super().__init__(qualified_behavior_name) + self.qualified_behavior_name = qualified_behavior_name + + def enter_node(self, listener): + if hasattr(listener, "enter_action_declaration"): + listener.enter_action_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_action_declaration"): + listener.exit_action_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_action_declaration"): + return visitor.visit_action_declaration(self) + else: + return visitor.visit_children(self) + + def get_base_type(self): + inherits = self.find_first_child_of_type(ActionInherits) + if inherits: + return inherits.qualified_behavior_name + return None + + +class ActionInherits(Inheritance): + + def __init__(self, qualified_behavior_name): + super().__init__() + self.qualified_behavior_name = qualified_behavior_name + + def enter_node(self, listener): + if hasattr(listener, "enter_action_inherits"): + listener.enter_action_inherits(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_action_inherits"): + listener.exit_action_inherits(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_action_inherits"): + return visitor.visit_action_inherits(self) + else: + return visitor.visit_children(self) + + +class ModifierDeclaration(Declaration): + + def __init__(self, actor_name, modifier_name): + super().__init__() + self.actor = actor_name + self.modifier = modifier_name + + def enter_node(self, listener): + if hasattr(listener, "enter_modifier_declaration"): + listener.enter_modifier_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_modifier_declaration"): + listener.exit_modifier_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_modifier_declaration"): + return visitor.visit_modifier_declaration(self) + else: + return visitor.visit_children(self) + + +class EnumTypeExtension(Declaration): + + def __init__(self, enum_name): + super().__init__() + self.enum_name = enum_name + + def enter_node(self, listener): + if hasattr(listener, "enter_enum_type_extension"): + listener.enter_enum_type_extension(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_enum_type_extension"): + listener.exit_enum_type_extension(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_enum_type_extension"): + return visitor.visit_enum_type_extension(self) + else: + return visitor.visit_children(self) + + +class StructuredTypeExtension(Declaration): + + def __init__(self, type_name, qualified_behavior_name): + super().__init__() + self.type_name = type_name + self.qualified_behavior_name = qualified_behavior_name + + def enter_node(self, listener): + if hasattr(listener, "enter_structured_type_extension"): + listener.enter_structured_type_extension(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_structured_type_extension"): + listener.exit_structured_type_extension(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_structured_type_extension"): + return visitor.visit_structured_type_extension(self) + else: + return visitor.visit_children(self) + + +class GlobalParameterDeclaration(Parameter): + + def __init__(self, name, field_name, field_type, default_value): + super().__init__(name) + self.field_name = field_name + self.field_type = field_type + self.default_value = default_value + + def enter_node(self, listener): + if hasattr(listener, "enter_global_parameter_declaration"): + listener.enter_global_parameter_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_global_parameter_declaration"): + listener.exit_global_parameter_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_global_parameter_declaration"): + return visitor.visit_global_parameter_declaration(self) + else: + return visitor.visit_children(self) + + +class ParameterDeclaration(Parameter): + + def __init__(self, field_name, field_type, default_value): + super().__init__(field_name) + self.field_name = field_name + self.field_type = field_type + self.default_value = default_value + + def enter_node(self, listener): + if hasattr(listener, "enter_parameter_declaration"): + listener.enter_parameter_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_parameter_declaration"): + listener.exit_parameter_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_parameter_declaration"): + return visitor.visit_parameter_declaration(self) + else: + return visitor.visit_children(self) + + +class ParameterReference(ModelElement): + + def __init__(self, field_name, field_access): + super().__init__() + self.field_name = field_name + self.field_access = field_access + + def enter_node(self, listener): + if hasattr(listener, "enter_parameter_reference"): + listener.enter_parameter_reference(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_parameter_reference"): + listener.exit_parameter_reference(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_parameter_reference"): + return visitor.visit_parameter_reference(self) + else: + return visitor.visit_children(self) + + +class EventDeclaration(Declaration): + + def __init__(self, event_name): + super().__init__(event_name) + self.field_name = event_name + + def enter_node(self, listener): + if hasattr(listener, "enter_event_declaration"): + listener.enter_event_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_event_declaration"): + listener.exit_event_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_event_declaration"): + return visitor.visit_event_declaration(self) + else: + return visitor.visit_children(self) + + +class EventReference(ModelElement): + + def __init__(self, event_path): + super().__init__() + self.event_path = event_path + + def enter_node(self, listener): + if hasattr(listener, "enter_event_reference"): + listener.enter_event_reference(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_event_reference"): + listener.exit_event_reference(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_event_reference"): + return visitor.visit_event_reference(self) + else: + return visitor.visit_children(self) + + +class EventFieldDecl(ModelElement): + + def __init__(self, event_field_name): + super().__init__() + self.event_field_name = event_field_name + + def enter_node(self, listener): + if hasattr(listener, "enter_event_field_declaration"): + listener.enter_event_field_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_event_field_declaration"): + listener.exit_event_field_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_event_field_declaration"): + return visitor.visit_event_field_declaration(self) + else: + return visitor.visit_children(self) + + +class EventCondition(ModelElement): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_event_condition"): + listener.enter_event_condition(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_event_condition"): + listener.exit_event_condition(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_event_condition"): + return visitor.visit_event_condition(self) + else: + return visitor.visit_children(self) + + +class MethodDeclaration(Declaration): + + def __init__(self, method_name, return_type): + super().__init__() + self.method_name = method_name + self.return_type = return_type + + def enter_node(self, listener): + if hasattr(listener, "enter_method_declaration"): + listener.enter_method_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_method_declaration"): + listener.exit_method_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_method_declaration"): + return visitor.visit_method_declaration(self) + else: + return visitor.visit_children(self) + + +class MethodBody(ModelElement): + + def __init__(self, qualifier, type_ref, external_name): + super().__init__() + self.qualifier = qualifier + self.type_ref = type_ref + self.external_name = external_name + + def enter_node(self, listener): + if hasattr(listener, "enter_method_body"): + listener.enter_method_body(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_method_body"): + listener.exit_method_body(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_method_body"): + return visitor.visit_method_body(self) + else: + return visitor.visit_children(self) + + +class CoverDeclaration(Declaration): + + def __init__(self, target_name): + super().__init__() + self.target_name = target_name + + def enter_node(self, listener): + if hasattr(listener, "enter_cover_declaration"): + listener.enter_cover_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_cover_declaration"): + listener.exit_cover_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_cover_declaration"): + return visitor.visit_cover_declaration(self) + else: + return visitor.visit_children(self) + + +class RecordDeclaration(Declaration): + + def __init__(self, target_name): + super().__init__() + self.target_name = target_name + + def enter_node(self, listener): + if hasattr(listener, "enter_record_declaration"): + listener.enter_record_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_record_declaration"): + listener.exit_record_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_record_declaration"): + return visitor.visit_record_declaration(self) + else: + return visitor.visit_children(self) + + +class Argument(ModelElement): + + def __init__(self, argument_name, argument_type, default_value): + super().__init__() + self.argument_name = argument_name + self.argument_type = argument_type + self.default_value = default_value + + def enter_node(self, listener): + if hasattr(listener, "enter_argument"): + listener.enter_argument(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_argument"): + listener.exit_argument(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_argument"): + return visitor.visit_argument(self) + else: + return visitor.visit_children(self) + + +class NamedArgument(ModelElement): + + def __init__(self, argument_name): + super().__init__(argument_name) + self.argument_name = argument_name + + def enter_node(self, listener): + if hasattr(listener, "enter_named_argument"): + listener.enter_named_argument(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_named_argument"): + listener.exit_named_argument(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_named_argument"): + return visitor.visit_named_argument(self) + else: + return visitor.visit_children(self) + + def get_resolved_value(self): + if self.get_child_count() != 1: + raise OSC2ParsingError( + msg=f'Could not get value of positional argument because the expected child count is not 1, but {self.get_child_count()}.', context=self.get_ctx()) + return self.get_child(0).get_resolved_value() + + +class PositionalArgument(ModelElement): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_positional_argument"): + listener.enter_positional_argument(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_positional_argument"): + listener.exit_positional_argument(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_positional_argument"): + return visitor.visit_positional_argument(self) + else: + return visitor.visit_children(self) + + def get_resolved_value(self): + if self.get_child_count() != 1: + raise OSC2ParsingError( + msg=f'Could not get value of positional argument because the expected child count is not 1, but {self.get_child_count()}.', context=self.get_ctx()) + return self.get_child(0).get_resolved_value() + + +class VariableDeclaration(Parameter): + + def __init__(self, field_name, field_type, default_value): + super().__init__() + self.field_name = field_name # unused? + self.field_type = field_type + self.default_value = default_value + # self.set_children(field_name) + + def enter_node(self, listener): + if hasattr(listener, "enter_variable_declaration"): + listener.enter_variable_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_variable_declaration"): + listener.exit_variable_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_variable_declaration"): + return visitor.visit_variable_declaration(self) + else: + return visitor.visit_children(self) + + +class KeepConstraintDeclaration(Declaration): + + def __init__(self, constraint_qualifier): + super().__init__() + self.constraint_qualifier = constraint_qualifier + + def enter_node(self, listener): + if hasattr(listener, "enter_keep_constraint_declaration"): + listener.enter_keep_constraint_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_keep_constraint_declaration"): + listener.exit_keep_constraint_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_keep_constraint_declaration"): + return visitor.visit_keep_constraint_declaration(self) + else: + return visitor.visit_children(self) + + def get_resolved_value(self): + result = None + if self.get_child_count() == 1 and isinstance(self.get_child(0), RelationExpression) and self.get_child(0).get_child_count() == 2: + if self.get_child(0).operator != "==": + raise OSC2ParsingError( + msg=f'Only relation "==" is currently supported in "keep".', context=self.get_ctx()) + field_exp = self.get_child(0).get_child_with_expected_type(0, FieldAccessExpression) + value = self.get_child(0).get_child(1).get_resolved_value() + + if not field_exp.field_name.startswith('it.'): + raise OSC2ParsingError( + msg=f'FieldAccessExpression only supports "it." prefix, not "{field_exp.field_name}".', context=self.get_ctx()) + field_name = field_exp.field_name.removeprefix("it.") + result = {} + member_path = field_name.split('.') + current_params = result + for current_pos in range(0, len(member_path)): # pylint: disable=consider-using-enumerate + if current_pos != len(member_path) - 1: + current_params[member_path[current_pos]] = {} + current_params = current_params[member_path[current_pos]] + else: + current_params[member_path[current_pos]] = value + else: + raise OSC2ParsingError( + msg=f'Keep uses unsupported expression: allowed "==" only.', context=self.get_ctx()) + + if result is None: + raise OSC2ParsingError( + msg=f'Error in keep constraint declaration.', context=self.get_ctx()) + + return result + + +class RemoveDefaultDeclaration(Declaration): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_remove_default_declaration"): + listener.enter_remove_default_declaration(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_remove_default_declaration"): + listener.exit_remove_default_declaration(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_remove_default_declaration"): + return visitor.visit_remove_default_declaration(self) + else: + return visitor.visit_children(self) + + +class OnDirective(ModelElement): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_on_directive"): + listener.enter_on_directive(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_on_directive"): + listener.exit_on_directive(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_on_directive"): + return visitor.visit_on_directive(self) + else: + return visitor.visit_children(self) + + +class DoDirective(ModelElement): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_do_directive"): + listener.enter_do_directive(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_do_directive"): + listener.exit_do_directive(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_do_directive"): + return visitor.visit_do_directive(self) + else: + return visitor.visit_children(self) + + +class DoMember(ModelElement): + + def __init__(self, label_name, composition_operator): + super().__init__(label_name) + self.label_name = label_name + self.composition_operator = composition_operator + + def enter_node(self, listener): + if hasattr(listener, "enter_do_member"): + listener.enter_do_member(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_do_member"): + listener.exit_do_member(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_do_member"): + return visitor.visit_do_member(self) + else: + return visitor.visit_children(self) + + +class WaitDirective(ModelElement): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_wait_directive"): + listener.enter_wait_directive(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_wait_directive"): + listener.exit_wait_directive(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_wait_directive"): + return visitor.visit_wait_directive(self) + else: + return visitor.visit_children(self) + + +class EmitDirective(ModelElement): + + def __init__(self, event_name): + super().__init__(event_name) + self.event_name = event_name + + def enter_node(self, listener): + if hasattr(listener, "enter_emit_directive"): + listener.enter_emit_directive(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_emit_directive"): + listener.exit_emit_directive(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_emit_directive"): + return visitor.visit_emit_directive(self) + else: + return visitor.visit_children(self) + + +class CallDirective(ModelElement): + + def __init__(self, method_name): + super().__init__() + self.method_name = method_name + + def enter_node(self, listener): + if hasattr(listener, "enter_call_directive"): + listener.enter_call_directive(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_call_directive"): + listener.exit_call_directive(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_call_directive"): + return visitor.visit_call_directive(self) + else: + return visitor.visit_children(self) + + +class UntilDirective(ModelElement): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_until_directive"): + listener.enter_until_directive(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_until_directive"): + listener.exit_until_directive(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_until_directive"): + return visitor.visit_until_directive(self) + else: + return visitor.visit_children(self) + + +class BehaviorInvocation(StructuredDeclaration): + + def __init__(self, name, actor, behavior): + super().__init__(name) + self.actor = actor + self.behavior = behavior + + def enter_node(self, listener): + if hasattr(listener, "enter_behavior_invocation"): + listener.enter_behavior_invocation(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_behavior_invocation"): + listener.exit_behavior_invocation(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_behavior_invocation"): + return visitor.visit_behavior_invocation(self) + else: + return visitor.visit_children(self) + + def get_base_type(self): + return self.behavior + + def get_type(self): + return self.behavior, False + + +class ModifierInvocation(ModelElement): + + def __init__(self, actor, modifier_name): + super().__init__() + self.actor = actor + self.modifier_name = modifier_name + # self.set_children(actor, modifier_name) + + def enter_node(self, listener): + if hasattr(listener, "enter_modifier_invocation"): + listener.enter_modifier_invocation(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_modifier_invocation"): + listener.exit_modifier_invocation(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_modifier_invocation"): + return visitor.visit_modifier_invocation(self) + else: + return visitor.visit_children(self) + + +class RiseExpression(Expression): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_rise_expression"): + listener.enter_rise_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_rise_expression"): + listener.exit_rise_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_rise_expression"): + return visitor.visit_rise_expression(self) + else: + return visitor.visit_children(self) + + +class FallExpression(Expression): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_fall_expression"): + listener.enter_rise_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_fall_expression"): + listener.exit_fall_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_fall_expression"): + return visitor.visit_fall_expression(self) + else: + return visitor.visit_children(self) + + +class ElapsedExpression(Expression): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_elapsed_expression"): + listener.enter_rise_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_elapsed_expression"): + listener.exit_fall_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_elapsed_expression"): + return visitor.visit_fall_expression(self) + else: + return visitor.visit_children(self) + + +class EveryExpression(Expression): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_every_expression"): + listener.enter_every_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_every_expression"): + listener.exit_every_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_every_expression"): + return visitor.visit_every_expression(self) + else: + return visitor.visit_children(self) + + +class SampleExpression(Expression): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_sample_expression"): + listener.enter_sample_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_sample_expression"): + listener.exit_sample_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_sample_expression"): + return visitor.visit_sample_expression(self) + else: + return visitor.visit_children(self) + + +class CastExpression(Expression): + + def __init__(self, object_def, target_type): + super().__init__() + self.object_def = object_def + self.target_type = target_type + + def enter_node(self, listener): + if hasattr(listener, "enter_cast_expression"): + listener.enter_cast_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_cast_expression"): + listener.exit_cast_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_cast_expression"): + return visitor.visit_cast_expression(self) + else: + return visitor.visit_children(self) + + +class TypeTestExpression(Expression): + + def __init__(self, object_def, target_type): + super().__init__() + self.object_def = object_def + self.target_type = target_type + + def enter_node(self, listener): + if hasattr(listener, "enter_type_test_expression"): + listener.enter_type_test_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_type_test_expression"): + listener.exit_type_test_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_type_test_expression"): + return visitor.visit_type_test_expression(self) + else: + return visitor.visit_children(self) + + +class ElementAccessExpression(Expression): + + def __init__(self, list_name, index): + super().__init__() + self.list_name = list_name + self.index = index + + def enter_node(self, listener): + if hasattr(listener, "enter_element_access_expression"): + listener.enter_element_access_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_element_access_expression"): + listener.exit_element_access_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_element_access_expression"): + return visitor.visit_element_access_expression(self) + else: + return visitor.visit_children(self) + + +class FunctionApplicationExpression(Expression): + + def __init__(self, func_name): + super().__init__() + self.func_name = func_name + + def enter_node(self, listener): + if hasattr(listener, "enter_function_application_expression"): + listener.enter_function_application_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_function_application_expression"): + listener.exit_function_application_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_function_application_expression"): + return visitor.visit_function_application_expression(self) + else: + return visitor.visit_children(self) + + def get_resolved_value(self): + ref = self.find_first_child_of_type(IdentifierReference) + params = ref.get_resolved_value() + + named = False + pos = 0 + param_keys = list(params.keys()) + for child in self.get_children(): + key = None + if isinstance(child, PositionalArgument): + if named: + raise OSC2ParsingError( + msg=f'Positional argument after named argument not allowed.', context=child.get_ctx()) + key = param_keys[pos] + elif isinstance(child, NamedArgument): + named = True + key = child.name + if key: + params[key] = child.get_resolved_value() + return params + + def get_type(self): + ref = self.find_first_child_of_type(IdentifierReference) + return ref.get_type() + + def get_type_string(self): + return self.get_type()[0].name + + +class FieldAccessExpression(Expression): + + def __init__(self, field_name): + super().__init__() + self.field_name = field_name + + def enter_node(self, listener): + if hasattr(listener, "enter_field_access_expression"): + listener.enter_field_access_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_field_access_expression"): + listener.exit_field_access_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_field_access_expression"): + return visitor.visit_field_access_expression(self) + else: + return visitor.visit_children(self) + + +class BinaryExpression(Expression): + + def __init__(self, operator): + super().__init__() + self.operator = operator + + def enter_node(self, listener): + if hasattr(listener, "enter_binary_expression"): + listener.enter_binary_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_binary_expression"): + listener.exit_binary_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_binary_expression"): + return visitor.visit_binary_expression(self) + else: + return visitor.visit_children(self) + + +class UnaryExpression(Expression): + + def __init__(self, operator): + super().__init__() + self.operator = operator + + def enter_node(self, listener): + if hasattr(listener, "enter_unary_expression"): + listener.enter_unary_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_unary_expression"): + listener.exit_unary_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_unary_expression"): + return visitor.visit_unary_expression(self) + else: + return visitor.visit_children(self) + + +class TernaryExpression(Expression): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_ternary_expression"): + listener.enter_ternary_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_ternary_expression"): + listener.exit_ternary_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_ternary_expression"): + return visitor.visit_ternary_expression(self) + else: + return visitor.visit_children(self) + + +class LogicalExpression(Expression): + + def __init__(self, operator): + super().__init__() + self.operator = operator + + def enter_node(self, listener): + if hasattr(listener, "enter_logical_expression"): + listener.enter_logical_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_logical_expression"): + listener.exit_logical_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_logical_expression"): + return visitor.visit_logical_expression(self) + else: + return visitor.visit_children(self) + + +class RelationExpression(Expression): + + def __init__(self, operator): + super().__init__() + self.operator = operator + + def enter_node(self, listener): + if hasattr(listener, "enter_relation_expression"): + listener.enter_relation_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_relation_expression"): + listener.exit_relation_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_relation_expression"): + return visitor.visit_relation_expression(self) + else: + return visitor.visit_children(self) + + +class ListExpression(Expression): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_list_expression"): + listener.enter_list_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_list_expression"): + listener.exit_list_literal(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_list_expression"): + return visitor.visit_list_expression(self) + else: + return visitor.visit_children(self) + + def get_type_string(self): + child = None + if self.get_child_count(): + child = self.get_child(0) + if not child: + raise OSC2ParsingError(msg='At least on child expected.', context=self.get_ctx()) + type_string = child.get_type_string() + return "listof" + type_string + + def get_resolved_value(self): + value = [] + for child in self.get_children(): + value.append(child.get_resolved_value()) + return value + + +class RangeExpression(Expression): + + def __init__(self): + super().__init__() + + def enter_node(self, listener): + if hasattr(listener, "enter_range_expression"): + listener.enter_range_expression(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_range_expression"): + listener.exit_range_expression(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_range_expression"): + return visitor.visit_range_expression(self) + else: + return visitor.visit_children(self) + + +class PhysicalLiteral(ModelElement): + + def __init__(self, unit, value): + super().__init__() + self.value = value + self.unit = unit + + def enter_node(self, listener): + if hasattr(listener, "enter_physical_literal"): + listener.enter_physical_literal(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_physical_literal"): + listener.exit_physical_literal(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_physical_literal"): + return visitor.visit_physical_literal(self) + else: + return visitor.visit_children(self) + + def get_value_child(self): + return self.get_child(0) + + def get_type_string(self): + return self.unit.get_type_string() + + def get_resolved_value(self): + si_unit_specifier = self.unit.find_first_child_of_type(SIUnitSpecifier) + return self.get_value_child().get_resolved_value() * si_unit_specifier.factor + + +class IntegerLiteral(ModelElement): + + def __init__(self, type_def, value): + super().__init__() + self.type_def = type_def # uint, hex, int + self.value = value + + def enter_node(self, listener): + if hasattr(listener, "enter_integer_literal"): + listener.enter_integer_literal(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_integer_literal"): + listener.exit_integer_literal(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_integer_literal"): + return visitor.visit_integer_literal(self) + else: + return visitor.visit_children(self) + + def get_type_string(self): + return 'int' + + def get_resolved_value(self): + return int(self.value) + + +class FloatLiteral(ModelElement): + + def __init__(self, value): + super().__init__() + self.value = value + + def enter_node(self, listener): + if hasattr(listener, "enter_float_literal"): + listener.enter_float_literal(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_float_literal"): + listener.exit_float_literal(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_float_literal"): + return visitor.visit_float_literal(self) + else: + return visitor.visit_children(self) + + def get_type_string(self): + return 'float' + + def get_resolved_value(self): + return float(self.value) + + +class BoolLiteral(ModelElement): + + def __init__(self, value): + super().__init__() + self.value = value + + def enter_node(self, listener): + if hasattr(listener, "enter_bool_literal"): + listener.enter_bool_literal(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_bool_literal"): + listener.exit_bool_literal(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_bool_literal"): + return visitor.visit_bool_literal(self) + else: + return visitor.visit_children(self) + + def get_type_string(self): + return 'bool' + + def get_resolved_value(self): + return self.value == "true" + + +class StringLiteral(ModelElement): + + def __init__(self, value): + super().__init__() + self.value = value + + def get_value_child(self): + return self.value + + def get_resolved_value(self): + return self.value.strip("\'").strip("\"") + + def enter_node(self, listener): + if hasattr(listener, "enter_string_literal"): + listener.enter_string_literal(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_string_literal"): + listener.exit_string_literal(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_string_literal"): + return visitor.visit_string_literal(self) + else: + return visitor.visit_children(self) + + def get_type_string(self): + return 'string' + + +class Type(ModelElement): + + def __init__(self, type_def, is_list): + super().__init__() + self.type_def = type_def + self.is_list = is_list + + def enter_node(self, listener): + if hasattr(listener, "enter_type"): + listener.enter_type(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_type"): + listener.exit_type(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_type"): + return visitor.visit_type(self) + else: + return visitor.visit_children(self) + + def get_resolved_value(self): + return None + + +class Identifier(ModelElement): + + def __init__(self, name): + super().__init__() + self.name = name + + def enter_node(self, listener): + if hasattr(listener, "enter_identifier"): + listener.enter_identifier(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_identifier"): + listener.exit_identifier(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_identifier"): + return visitor.visit_identifier(self) + else: + return visitor.visit_children(self) + + +class IdentifierReference(ModelElement): + + def __init__(self, ref): + super().__init__() + self.ref = ref + + def enter_node(self, listener): + if hasattr(listener, "enter_identifier_reference"): + listener.enter_identifier_reference(self) + + def exit_node(self, listener): + if hasattr(listener, "exit_identifier_reference"): + listener.exit_identifier_reference(self) + + def accept(self, visitor): + if hasattr(visitor, "visit_identifier_reference"): + return visitor.visit_identifier_reference(self) + else: + return visitor.visit_children(self) + + def get_type(self): + return self.ref.get_type() + + def get_type_string(self): + return self.ref.get_type_string() + + def get_resolved_value(self): + return self.ref.get_resolved_value() diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/.gitignore b/scenario_execution_base/scenario_execution_base/osc2_parsing/.gitignore new file mode 100644 index 00000000..91827d60 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/.gitignore @@ -0,0 +1 @@ +.antlr/ \ No newline at end of file diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.g4 b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.g4 new file mode 100644 index 00000000..e0fcd429 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.g4 @@ -0,0 +1,759 @@ +// MIT License + +// Copyright (c) 2018 CARLA + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Copyright (C) 2024 Intel Corporation + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions +// and limitations under the License. + +// SPDX-License-Identifier: Apache-2.0 + +grammar OpenSCENARIO2; + +tokens { INDENT, DEDENT} + + +@lexer::header{ +from antlr4.Token import CommonToken +import re +import importlib +# Allow languages to extend the lexer and parser, by loading the parser dynamically +module_path = __name__[:-5] +language_name = __name__.split('.')[-1] +language_name = language_name[:-5] # Remove Lexer from name +LanguageParser = getattr(importlib.import_module('{}Parser'.format(module_path)), '{}Parser'.format(language_name)) +} + +@lexer::members { + +@property +def tokens(self): + try: + return self._tokens + except AttributeError: + self._tokens = [] + return self._tokens + +@property +def indents(self): + try: + return self._indents + except AttributeError: + self._indents = [] + return self._indents + +@property +def opened(self): + try: + return self._opened + except AttributeError: + self._opened = 0 + return self._opened + +@opened.setter +def opened(self, value): + self._opened = value + +@property +def lastToken(self): + try: + return self._lastToken + except AttributeError: + self._lastToken = None + return self._lastToken + +@lastToken.setter +def lastToken(self, value): + self._lastToken = value + +def reset(self): + super().reset() + self.tokens = [] + self.indents = [] + self.opened = 0 + self.lastToken = None + +def emitToken(self, t): + super().emitToken(t) + self.tokens.append(t) + +def nextToken(self): + if self._input.LA(1) == Token.EOF and self.indents: + for i in range(len(self.tokens)-1,-1,-1): + if self.tokens[i].type == Token.EOF: + self.tokens.pop(i) + self.emitToken(self.commonToken(LanguageParser.NEWLINE, '\n')) + while self.indents: + self.emitToken(self.createDedent()) + self.indents.pop() + self.emitToken(self.commonToken(LanguageParser.EOF, "")) + next = super().nextToken() + if next.channel == Token.DEFAULT_CHANNEL: + self.lastToken = next + return next if not self.tokens else self.tokens.pop(0) + +def createDedent(self): + dedent = self.commonToken(LanguageParser.DEDENT, "") + dedent.line = self.lastToken.line + return dedent + +def commonToken(self, type, text, indent=0): + stop = self.getCharIndex()-1-indent + start = (stop - len(text) + 1) if text else stop + return CommonToken(self._tokenFactorySourcePair, type, super().DEFAULT_TOKEN_CHANNEL, start, stop) + +@staticmethod +def getIndentationCount(spaces): + count = 0 + for ch in spaces: + if ch == '\t': + count += 8 - (count % 8) + else: + count += 1 + return count + +def atStartOfInput(self): + return Lexer.column.fget(self) == 0 and Lexer.line.fget(self) == 1 + +} + + +//---------------------------------------- +// Parser rules +/*Top-Level structure*/ +osc_file : preludeStatement* oscDeclaration* EOF; + +preludeStatement : importStatement ; + +importStatement + : 'import' importReference NEWLINE + | NEWLINE; + +importReference + : StringLiteral + | structuredIdentifier + ; + +structuredIdentifier + : Identifier + | structuredIdentifier'.'Identifier + ; + +oscDeclaration + : physicalTypeDeclaration + | unitDeclaration + | enumDeclaration + | structDeclaration + | actorDeclaration + | actionDeclaration + | scenarioDeclaration + | modifierDeclaration + | typeExtension + | globalParameterDeclaration + | NEWLINE + ; + +//---------------------------------------- +// physicalTypeDeclaration +physicalTypeDeclaration : 'type' physicalTypeName 'is' baseUnitSpecifier NEWLINE; + +physicalTypeName : Identifier; + +baseUnitSpecifier : sIBaseUnitSpecifier; + +sIBaseUnitSpecifier : 'SI' OPEN_PAREN siBaseExponentList CLOSE_PAREN; + +//---------------------------------------- +// unitDeclaration +unitDeclaration : 'unit' unitName 'of' physicalTypeName 'is' unitSpecifier NEWLINE; + +unitSpecifier : siUnitSpecifier ; +unitName : Identifier | siBaseUnitName ; + +siBaseExponentList : siBaseExponent (',' siBaseExponent)* ; +siBaseExponent : siBaseUnitName ':' integerLiteral ; + +siUnitSpecifier : 'SI' '(' siBaseExponentList (',' siFactor)? (',' siOffset)? ')' ; +siFactor : 'factor' ':' ( FloatLiteral | integerLiteral ) ; +siOffset : 'offset' ':' ( FloatLiteral | integerLiteral ) ; +siBaseUnitName : 'kg' | 'm' | 's' | 'A' | 'K' | 'mol' | 'cd' | 'rad' ; + +//---------------------------------------- +// enumDeclaration +enumDeclaration : 'enum' enumName ':' OPEN_BRACK enumMemberDecl (',' enumMemberDecl)* CLOSE_BRACK NEWLINE; + +enumMemberDecl : enumMemberName ('=' enumMemberValue )?; + +enumMemberValue: UintLiteral | HexUintLiteral; + +enumName : Identifier; + +enumMemberName : Identifier; + +enumValueReference : enumName '!' enumMemberName; + +//---------------------------------------- +inheritsCondition : OPEN_PAREN fieldName '==' (enumValueReference | BoolLiteral) CLOSE_PAREN ; + +//---------------------------------------- +// structDeclaration +structDeclaration : + 'struct' structName (structInherits)? + ((':' NEWLINE INDENT structMemberDecl+ DEDENT) | NEWLINE); + +structInherits : 'inherits' structName (inheritsCondition)? ; + +structMemberDecl + : eventDeclaration + | fieldDeclaration + | constraintDeclaration + | methodDeclaration + | coverageDeclaration; + +fieldName : Identifier; + +structName : Identifier; + +//---------------------------------------- +// actorDeclaration +actorDeclaration : + 'actor' actorName (actorInherits)? + ((':' NEWLINE INDENT actorMemberDecl+ DEDENT) | NEWLINE); + +actorInherits : 'inherits' actorName (inheritsCondition)? ; + +actorMemberDecl + : eventDeclaration + | fieldDeclaration + | constraintDeclaration + | methodDeclaration + | coverageDeclaration; + +actorName : Identifier; + +//---------------------------------------- +// scenarioDeclaration +scenarioDeclaration + : 'scenario' qualifiedBehaviorName (scenarioInherits)? + ((':' NEWLINE INDENT + (scenarioMemberDecl | behaviorSpecification )+ + DEDENT) | NEWLINE); + +scenarioInherits : 'inherits' qualifiedBehaviorName (inheritsCondition)? ; + +scenarioMemberDecl + : eventDeclaration + | fieldDeclaration + | constraintDeclaration + | methodDeclaration + | coverageDeclaration + | modifierInvocation; + +qualifiedBehaviorName : (actorName '.')? behaviorName; + +behaviorName : Identifier; + +//---------------------------------------- +// actionDeclaration +actionDeclaration + : 'action' qualifiedBehaviorName (actionInherits)? + ((':' NEWLINE INDENT (scenarioMemberDecl | behaviorSpecification)+ DEDENT) | NEWLINE); + +actionInherits : 'inherits' qualifiedBehaviorName (inheritsCondition)? ; + +//---------------------------------------- +// modifierDeclaration +modifierDeclaration + : 'modifier' (actorName '.')? modifierName ('of' qualifiedBehaviorName)? + ((':' NEWLINE INDENT scenarioMemberDecl+ DEDENT) | NEWLINE); + +modifierName : Identifier; + +//---------------------------------------- +// typeExtension +typeExtension : enumTypeExtension | structuredTypeExtension; + +/* +enumTypeExtension : 'extend' enumName ':' NEWLINE INDENT + (enumMemberDecl NEWLINE)+ DEDENT; +*/ +enumTypeExtension : 'extend' enumName ':' OPEN_BRACK enumMemberDecl (',' enumMemberDecl)* CLOSE_BRACK NEWLINE; + + +structuredTypeExtension : 'extend' extendableTypeName + ':' NEWLINE INDENT extensionMemberDecl+ DEDENT; + + +extendableTypeName + : typeName + | qualifiedBehaviorName; + +extensionMemberDecl + : structMemberDecl + | actorMemberDecl + | scenarioMemberDecl + | behaviorSpecification; + +//---------------------------------------- +// globalParameterDeclaration +globalParameterDeclaration : 'global' fieldName (',' fieldName)* ':' typeDeclarator ('=' defaultValue)? (parameterWithDeclaration | NEWLINE); + +//Type declarations +typeDeclarator : nonAggregateTypeDeclarator | aggregateTypeDeclarator; + +nonAggregateTypeDeclarator : primitiveType | typeName | qualifiedBehaviorName; + +aggregateTypeDeclarator : listTypeDeclarator; + +listTypeDeclarator : 'list' 'of' nonAggregateTypeDeclarator; + +primitiveType : 'int' | 'uint' | 'float' | 'bool' | 'string'; + +typeName : Identifier; + +// Structured type members + +// eventDeclaration +eventDeclaration + : 'event' eventName + (OPEN_PAREN argumentListSpecification CLOSE_PAREN)? + ('is' eventSpecification)? NEWLINE; + +eventSpecification + : eventReference (( eventFieldDecl )? 'if' eventCondition) ? + | eventCondition ; + +eventReference : '@' eventPath; +eventFieldDecl : 'as' eventFieldName; +eventFieldName : Identifier; +eventName : Identifier; +eventPath : (expression '.')? eventName; + +eventCondition + : boolExpression + | riseExpression + | fallExpression + | elapsedExpression + | everyExpression; + +riseExpression : 'rise' OPEN_PAREN boolExpression CLOSE_PAREN; +fallExpression :'fall' OPEN_PAREN boolExpression CLOSE_PAREN; +elapsedExpression : 'elapsed' OPEN_PAREN durationExpression CLOSE_PAREN; + +//everyExpression : 'every' OPEN_PAREN durationExpression (',' 'offset' ':' durationExpression)? CLOSE_PAREN; +everyExpression : 'every' OPEN_PAREN durationExpression (',' Identifier{ +offsetName = $Identifier.text +if( not (offsetName == "offset") ): + print("%s must be offset" %offsetName) + raise NoViableAltException(self) +} ':' durationExpression)? CLOSE_PAREN; + +boolExpression : expression; +durationExpression : expression; + +// fieldDeclaration +fieldDeclaration + : parameterDeclaration + | variableDeclaration; + +//parameter-declaration ::= field-name (',' field-name)* ':' type-declarator ['=' default-value] [parameter-with-declaration] NEWLINE +//[improvement:] parameterWithDeclaration? NEWLINE -> (parameterWithDeclaration | NEWLINE) +parameterDeclaration + : fieldName (',' fieldName)* ':' typeDeclarator ('=' defaultValue)? (parameterWithDeclaration | NEWLINE); + +variableDeclaration + : 'var' fieldName (',' fieldName)* ':' typeDeclarator ('=' (sampleExpression | valueExp) )? NEWLINE; + +sampleExpression + : 'sample' OPEN_PAREN expression ',' eventSpecification (',' defaultValue)? CLOSE_PAREN; + +defaultValue : expression; + +parameterWithDeclaration : 'with' ':' NEWLINE INDENT + parameterWithMember+ DEDENT; + +// add coverageDeclaration +parameterWithMember : constraintDeclaration | coverageDeclaration; + +// constraintDeclaration +constraintDeclaration + : keepConstraintDeclaration | removeDefaultDeclaration; + +keepConstraintDeclaration + : 'keep' OPEN_PAREN (constraintQualifier)? constraintExpression CLOSE_PAREN NEWLINE; + +constraintQualifier + : 'default' | 'hard'; + +constraintExpression : expression; + +removeDefaultDeclaration : 'remove_default' OPEN_PAREN parameterReference CLOSE_PAREN NEWLINE; + +parameterReference : fieldName | fieldAccess ; + +modifierInvocation : ((behaviorExpression | actorExpression) '.')? modifierName OPEN_PAREN (argumentList)? CLOSE_PAREN NEWLINE; + +behaviorExpression : (actorExpression '.') behaviorName; + +// behaviorSpecification +behaviorSpecification : onDirective | doDirective; + +onDirective : 'on' eventSpecification ':' NEWLINE INDENT + onMember+ DEDENT; + +onMember : callDirective | emitDirective; + +doDirective : 'do' doMember; + +doMember + : (labelName ':')?(composition + | behaviorInvocation + | waitDirective + | emitDirective + | callDirective); + +// composition +composition : compositionOperator (OPEN_PAREN argumentList? CLOSE_PAREN)?':' NEWLINE INDENT + doMember+ DEDENT (behaviorWithDeclaration)?; + +compositionOperator + : 'serial' | 'one_of' | 'parallel'; + +behaviorInvocation + : (actorExpression '.')? behaviorName OPEN_PAREN (argumentList)? CLOSE_PAREN (behaviorWithDeclaration | NEWLINE); + +behaviorWithDeclaration : 'with' ':' NEWLINE INDENT + behaviorWithMember+ DEDENT; + +behaviorWithMember : constraintDeclaration + | modifierInvocation + | untilDirective; + +labelName : Identifier; + +actorExpression + : actorName; + +waitDirective + : 'wait' eventSpecification NEWLINE; + +emitDirective : 'emit' eventName (OPEN_PAREN argumentList CLOSE_PAREN)? NEWLINE; + +callDirective : 'call' methodInvocation NEWLINE; + +untilDirective : 'until' eventSpecification NEWLINE; + +methodInvocation : postfixExp OPEN_PAREN (argumentList)? CLOSE_PAREN; + +methodDeclaration : 'def' methodName OPEN_PAREN (argumentListSpecification)? CLOSE_PAREN ('->' returnType)? methodImplementation NEWLINE; + +returnType : typeDeclarator; + +methodImplementation + : 'is' (methodQualifier)? ('expression' expression + | 'undefined' + | 'external' structuredIdentifier OPEN_PAREN (argumentList)? CLOSE_PAREN); + +methodQualifier : 'only'; + +methodName : Identifier; + +coverageDeclaration: coverDeclaration | recordDeclaration ; + +coverDeclaration : 'cover' OPEN_PAREN targetName? coverageArgumentList* CLOSE_PAREN NEWLINE; + +recordDeclaration : 'record' OPEN_PAREN targetName? coverageArgumentList* CLOSE_PAREN NEWLINE; + +coverageArgumentList : (',' 'expression' ':' expression) #coverageExpression + | (',' 'unit' ':' unitName) #coverageUnit + | (',' 'range' ':' rangeConstructor) #coverageRange + | (',' 'every' ':' valueExp) #coverageEvery + | (',' 'event' ':' eventName) #coverageEvent + | (',' namedArgument) #coverageNameArgument + ; + +targetName : Identifier ; + +//Expressions +expression + : implication + | ternaryOpExp; + +ternaryOpExp + : implication '?' expression ':' expression; + +implication : disjunction ('=>' disjunction)*; +disjunction : conjunction ('or' conjunction)*; +conjunction : inversion ('and' inversion)*; +inversion + : 'not' inversion + | relation; + +relation + : sumExpression #sumExp + | relation relationalOp sumExpression #relationExp; + +relationalOp : '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in'; + +sumExpression + : term #termExp + | sumExpression additiveOp term #additiveExp; + +additiveOp + : '+' + | '-'; + +term + : factor #factorExp + | term multiplicativeOp factor #multiplicativeExp; + +multiplicativeOp + : '*' + | '/' + | '%'; + +factor + : postfixExp + | '-' factor; + +postfixExp + : primaryExp #primaryExpression + | postfixExp '.' 'as' OPEN_PAREN typeDeclarator CLOSE_PAREN #castExpression + | postfixExp '.' 'is' OPEN_PAREN typeDeclarator CLOSE_PAREN #typeTestExpression + | postfixExp OPEN_BRACK expression CLOSE_BRACK #elementAccessExpression + | postfixExp OPEN_PAREN (argumentList)? CLOSE_PAREN #functionApplicationExpression + | postfixExp '.' fieldName #fieldAccessExpression ; + +fieldAccess : postfixExp '.' fieldName ; + +primaryExp + : valueExp + | 'it' + | Identifier + | OPEN_PAREN expression CLOSE_PAREN; + +valueExp + : physicalLiteral + | FloatLiteral + | integerLiteral + | BoolLiteral + | StringLiteral + | identifierReference + | enumValueReference + | listConstructor + | rangeConstructor; + +listConstructor : OPEN_BRACK expression (',' expression)* CLOSE_BRACK; +rangeConstructor + : 'range' OPEN_PAREN expression ',' expression CLOSE_PAREN + | OPEN_BRACK expression '..' expression CLOSE_BRACK; + +identifierReference : (fieldName '.')* fieldName ; +//Common productions +argumentListSpecification : argumentSpecification (',' argumentSpecification)*; + +argumentSpecification : argumentName ':' typeDeclarator ('=' defaultValue)?; + +argumentName : Identifier; + +argumentList + : positionalArgument (',' positionalArgument)* (',' namedArgument)* + | namedArgument (',' namedArgument)*; + +positionalArgument : expression; +namedArgument : argumentName ':' expression; + +physicalLiteral : (FloatLiteral | integerLiteral) unitName; + +integerLiteral : UintLiteral | HexUintLiteral | IntLiteral; + +//---------------------------------------- +// Lexer rules + +NEWLINE + : ( {self.atStartOfInput()}? SPACES + | ( '\r'? '\n' | '\r' | '\f' ) SPACES? + ) + { +tempt = Lexer.text.fget(self) +newLine = re.sub("[^\r\n\f]+", "", tempt) +spaces = re.sub("[\r\n\f]+", "", tempt) +la_char = "" +try: + la = self._input.LA(1) + la_char = chr(la) # Python does not compare char to ints directly +except ValueError: # End of file + pass +# Strip newlines inside open clauses except if we are near EOF. We keep NEWLINEs near EOF to +# satisfy the final newline needed by the single_put rule used by the REPL. +try: + nextnext_la = self._input.LA(2) + nextnext_la_char = chr(nextnext_la) +except ValueError: + nextnext_eof = True +else: + nextnext_eof = False +if self.opened > 0 or nextnext_eof is False and (la_char == '\r' or la_char == '\n' or la_char == '\f' or la_char == '#'): + self.skip() +else: + indent = self.getIndentationCount(spaces) + previous = self.indents[-1] if self.indents else 0 + self.emitToken(self.commonToken(self.NEWLINE, newLine, indent=indent)) # NEWLINE is actually the '\n' char + if indent == previous: + self.skip() + elif indent > previous: + self.indents.append(indent) + self.emitToken(self.commonToken(LanguageParser.INDENT, spaces)) + else: + while self.indents and self.indents[-1] > indent: + self.emitToken(self.createDedent()) + self.indents.pop() + } + ; + +OPEN_BRACK : '[' {self.opened += 1} ; +CLOSE_BRACK : ']' {self.opened -= 1} ; +OPEN_PAREN : '(' {self.opened += 1} ; +CLOSE_PAREN : ')' {self.opened -= 1} ; + + +SKIP_ + : (SPACES | LINE_JOINING) + ->skip + ; + + fragment + SPACES + : [ \t]+ + ; + +fragment LINE_JOINING + : '\\' SPACES? '\r'? '\n' + ; + +fragment +RN + : '\r'? '\n' + ; + +BLOCK_COMMENT + : '/*' .*? '*/' + -> skip + ; + +LINE_COMMENT + : '#' ~[\r\n\f]* + -> skip + ; + +StringLiteral + : Shortstring + | Longstring + ; + +fragment +Shortstring + : ('"' ShortstringElem* '"') | ('\'' ShortstringElem* '\''); + + +fragment +ShortstringElem + : ShortstringChar | StringEscapeSeq; + +fragment +ShortstringChar + : ~[\\'"\r\n]; + + +fragment +Longstring + : ('"""' LongstringElem* '"""') | ('\'\'\'' LongstringElem* '\'\'\''); + +fragment +LongstringElem : LongstringChar | StringEscapeSeq; + +fragment +LongstringChar : ~'\\'; + +fragment +StringEscapeSeq + : '\\'. + | '\\' RN // [improvement:] consider: '\r'? '\n' + ; + + +FloatLiteral : [+-]? Digit* '.' Digit+ ([eE] [+-]? Digit+)?; + +UintLiteral : Digit+; + +HexUintLiteral : '0x' HexDigit+; + +IntLiteral : '-' Digit+; + +BoolLiteral : + 'true' | 'false'; + + +/* +where `id-start-char` matches all characters of the following Unicode character categories: + +* Ll -- Lowercase Letter +* Lm -- Modifier Letter +* Lo -- Other Letter +* Lt -- Titlecase Letter +* Lu -- Uppercase Letter +* Nl -- Letter Number + +It also matches the underscore character `_` (U+005F). + +`id-char` matches all characters that `id-start-char` matches, and additionally all characters of the following Unicode character categories: + +* Mc -- Spacing Combining Mark +* Mn -- Nonspacing Mark +* Nd -- Decimal Number +* Pc -- Connector Punctuations + +`non-vertical-line-char` matches all Unicode characters, except the vertical line `|` (U+007C) character. +*/ +Identifier : ( [A-Za-z] [A-Za-z0-9_]* ) | ( '|' (~[|])+ '|' ) ; + +fragment +NonVerticalLineChar : ~[\u007C]; + +fragment +Digit + : [0-9] + ; + +fragment +HexDigit + : [0-9A-Fa-f] + ; diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.interp b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.interp new file mode 100644 index 00000000..fb540c0f --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.interp @@ -0,0 +1,361 @@ +token literal names: +null +'import' +'.' +'type' +'is' +'SI' +'unit' +'of' +',' +':' +'factor' +'offset' +'kg' +'m' +'s' +'A' +'K' +'mol' +'cd' +'rad' +'enum' +'=' +'!' +'==' +'struct' +'inherits' +'actor' +'scenario' +'action' +'modifier' +'extend' +'global' +'list' +'int' +'uint' +'float' +'bool' +'string' +'event' +'if' +'@' +'as' +'rise' +'fall' +'elapsed' +'every' +'var' +'sample' +'with' +'keep' +'default' +'hard' +'remove_default' +'on' +'do' +'serial' +'one_of' +'parallel' +'wait' +'emit' +'call' +'until' +'def' +'->' +'expression' +'undefined' +'external' +'only' +'cover' +'record' +'range' +'?' +'=>' +'or' +'and' +'not' +'!=' +'<' +'<=' +'>' +'>=' +'in' +'+' +'-' +'*' +'/' +'%' +'it' +'..' +null +'[' +']' +'(' +')' +null +null +null +null +null +null +null +null +null +null +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +NEWLINE +OPEN_BRACK +CLOSE_BRACK +OPEN_PAREN +CLOSE_PAREN +SKIP_ +BLOCK_COMMENT +LINE_COMMENT +StringLiteral +FloatLiteral +UintLiteral +HexUintLiteral +IntLiteral +BoolLiteral +Identifier +INDENT +DEDENT + +rule names: +osc_file +preludeStatement +importStatement +importReference +structuredIdentifier +oscDeclaration +physicalTypeDeclaration +physicalTypeName +baseUnitSpecifier +sIBaseUnitSpecifier +unitDeclaration +unitSpecifier +unitName +siBaseExponentList +siBaseExponent +siUnitSpecifier +siFactor +siOffset +siBaseUnitName +enumDeclaration +enumMemberDecl +enumMemberValue +enumName +enumMemberName +enumValueReference +inheritsCondition +structDeclaration +structInherits +structMemberDecl +fieldName +structName +actorDeclaration +actorInherits +actorMemberDecl +actorName +scenarioDeclaration +scenarioInherits +scenarioMemberDecl +qualifiedBehaviorName +behaviorName +actionDeclaration +actionInherits +modifierDeclaration +modifierName +typeExtension +enumTypeExtension +structuredTypeExtension +extendableTypeName +extensionMemberDecl +globalParameterDeclaration +typeDeclarator +nonAggregateTypeDeclarator +aggregateTypeDeclarator +listTypeDeclarator +primitiveType +typeName +eventDeclaration +eventSpecification +eventReference +eventFieldDecl +eventFieldName +eventName +eventPath +eventCondition +riseExpression +fallExpression +elapsedExpression +everyExpression +boolExpression +durationExpression +fieldDeclaration +parameterDeclaration +variableDeclaration +sampleExpression +defaultValue +parameterWithDeclaration +parameterWithMember +constraintDeclaration +keepConstraintDeclaration +constraintQualifier +constraintExpression +removeDefaultDeclaration +parameterReference +modifierInvocation +behaviorExpression +behaviorSpecification +onDirective +onMember +doDirective +doMember +composition +compositionOperator +behaviorInvocation +behaviorWithDeclaration +behaviorWithMember +labelName +actorExpression +waitDirective +emitDirective +callDirective +untilDirective +methodInvocation +methodDeclaration +returnType +methodImplementation +methodQualifier +methodName +coverageDeclaration +coverDeclaration +recordDeclaration +coverageArgumentList +targetName +expression +ternaryOpExp +implication +disjunction +conjunction +inversion +relation +relationalOp +sumExpression +additiveOp +term +multiplicativeOp +factor +postfixExp +fieldAccess +primaryExp +valueExp +listConstructor +rangeConstructor +identifierReference +argumentListSpecification +argumentSpecification +argumentName +argumentList +positionalArgument +namedArgument +physicalLiteral +integerLiteral + + +atn: +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 107, 1329, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, 4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101, 4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106, 9, 106, 4, 107, 9, 107, 4, 108, 9, 108, 4, 109, 9, 109, 4, 110, 9, 110, 4, 111, 9, 111, 4, 112, 9, 112, 4, 113, 9, 113, 4, 114, 9, 114, 4, 115, 9, 115, 4, 116, 9, 116, 4, 117, 9, 117, 4, 118, 9, 118, 4, 119, 9, 119, 4, 120, 9, 120, 4, 121, 9, 121, 4, 122, 9, 122, 4, 123, 9, 123, 4, 124, 9, 124, 4, 125, 9, 125, 4, 126, 9, 126, 4, 127, 9, 127, 4, 128, 9, 128, 4, 129, 9, 129, 4, 130, 9, 130, 4, 131, 9, 131, 4, 132, 9, 132, 4, 133, 9, 133, 4, 134, 9, 134, 4, 135, 9, 135, 4, 136, 9, 136, 4, 137, 9, 137, 4, 138, 9, 138, 4, 139, 9, 139, 4, 140, 9, 140, 4, 141, 9, 141, 3, 2, 7, 2, 284, 10, 2, 12, 2, 14, 2, 287, 11, 2, 3, 2, 7, 2, 290, 10, 2, 12, 2, 14, 2, 293, 11, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 304, 10, 4, 3, 5, 3, 5, 5, 5, 308, 10, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 316, 10, 6, 12, 6, 14, 6, 319, 11, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 332, 10, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 14, 3, 14, 5, 14, 361, 10, 14, 3, 15, 3, 15, 3, 15, 7, 15, 366, 10, 15, 12, 15, 14, 15, 369, 11, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 5, 17, 380, 10, 17, 3, 17, 3, 17, 5, 17, 384, 10, 17, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 18, 5, 18, 392, 10, 18, 3, 19, 3, 19, 3, 19, 3, 19, 5, 19, 398, 10, 19, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 7, 21, 409, 10, 21, 12, 21, 14, 21, 412, 11, 21, 3, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 5, 22, 420, 10, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 5, 27, 437, 10, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 5, 28, 444, 10, 28, 3, 28, 3, 28, 3, 28, 3, 28, 6, 28, 450, 10, 28, 13, 28, 14, 28, 451, 3, 28, 3, 28, 3, 28, 5, 28, 457, 10, 28, 3, 29, 3, 29, 3, 29, 5, 29, 462, 10, 29, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 5, 30, 469, 10, 30, 3, 31, 3, 31, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 5, 33, 478, 10, 33, 3, 33, 3, 33, 3, 33, 3, 33, 6, 33, 484, 10, 33, 13, 33, 14, 33, 485, 3, 33, 3, 33, 3, 33, 5, 33, 491, 10, 33, 3, 34, 3, 34, 3, 34, 5, 34, 496, 10, 34, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 5, 35, 503, 10, 35, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 5, 37, 510, 10, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 6, 37, 517, 10, 37, 13, 37, 14, 37, 518, 3, 37, 3, 37, 3, 37, 5, 37, 524, 10, 37, 3, 38, 3, 38, 3, 38, 5, 38, 529, 10, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 5, 39, 537, 10, 39, 3, 40, 3, 40, 3, 40, 5, 40, 542, 10, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 5, 42, 551, 10, 42, 3, 42, 3, 42, 3, 42, 3, 42, 3, 42, 6, 42, 558, 10, 42, 13, 42, 14, 42, 559, 3, 42, 3, 42, 3, 42, 5, 42, 565, 10, 42, 3, 43, 3, 43, 3, 43, 5, 43, 570, 10, 43, 3, 44, 3, 44, 3, 44, 3, 44, 5, 44, 576, 10, 44, 3, 44, 3, 44, 3, 44, 5, 44, 581, 10, 44, 3, 44, 3, 44, 3, 44, 3, 44, 6, 44, 587, 10, 44, 13, 44, 14, 44, 588, 3, 44, 3, 44, 3, 44, 5, 44, 594, 10, 44, 3, 45, 3, 45, 3, 46, 3, 46, 5, 46, 600, 10, 46, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 7, 47, 609, 10, 47, 12, 47, 14, 47, 612, 11, 47, 3, 47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3, 48, 3, 48, 6, 48, 623, 10, 48, 13, 48, 14, 48, 624, 3, 48, 3, 48, 3, 49, 3, 49, 5, 49, 631, 10, 49, 3, 50, 3, 50, 3, 50, 3, 50, 5, 50, 637, 10, 50, 3, 51, 3, 51, 3, 51, 3, 51, 7, 51, 643, 10, 51, 12, 51, 14, 51, 646, 11, 51, 3, 51, 3, 51, 3, 51, 3, 51, 5, 51, 652, 10, 51, 3, 51, 3, 51, 5, 51, 656, 10, 51, 3, 52, 3, 52, 5, 52, 660, 10, 52, 3, 53, 3, 53, 3, 53, 5, 53, 665, 10, 53, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 5, 58, 683, 10, 58, 3, 58, 3, 58, 5, 58, 687, 10, 58, 3, 58, 3, 58, 3, 59, 3, 59, 5, 59, 693, 10, 59, 3, 59, 3, 59, 5, 59, 697, 10, 59, 3, 59, 5, 59, 700, 10, 59, 3, 60, 3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 62, 3, 62, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 5, 64, 715, 10, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 5, 65, 724, 10, 65, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 5, 69, 749, 10, 69, 3, 69, 3, 69, 3, 70, 3, 70, 3, 71, 3, 71, 3, 72, 3, 72, 5, 72, 759, 10, 72, 3, 73, 3, 73, 3, 73, 7, 73, 764, 10, 73, 12, 73, 14, 73, 767, 11, 73, 3, 73, 3, 73, 3, 73, 3, 73, 5, 73, 773, 10, 73, 3, 73, 3, 73, 5, 73, 777, 10, 73, 3, 74, 3, 74, 3, 74, 3, 74, 7, 74, 783, 10, 74, 12, 74, 14, 74, 786, 11, 74, 3, 74, 3, 74, 3, 74, 3, 74, 3, 74, 5, 74, 793, 10, 74, 5, 74, 795, 10, 74, 3, 74, 3, 74, 3, 75, 3, 75, 3, 75, 3, 75, 3, 75, 3, 75, 3, 75, 5, 75, 806, 10, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 77, 3, 77, 3, 77, 3, 77, 3, 77, 6, 77, 817, 10, 77, 13, 77, 14, 77, 818, 3, 77, 3, 77, 3, 78, 3, 78, 5, 78, 825, 10, 78, 3, 79, 3, 79, 5, 79, 829, 10, 79, 3, 80, 3, 80, 3, 80, 5, 80, 834, 10, 80, 3, 80, 3, 80, 3, 80, 3, 80, 3, 81, 3, 81, 3, 82, 3, 82, 3, 83, 3, 83, 3, 83, 3, 83, 3, 83, 3, 83, 3, 84, 3, 84, 5, 84, 852, 10, 84, 3, 85, 3, 85, 5, 85, 856, 10, 85, 3, 85, 3, 85, 5, 85, 860, 10, 85, 3, 85, 3, 85, 3, 85, 5, 85, 865, 10, 85, 3, 85, 3, 85, 3, 85, 3, 86, 3, 86, 3, 86, 3, 86, 3, 86, 3, 87, 3, 87, 5, 87, 877, 10, 87, 3, 88, 3, 88, 3, 88, 3, 88, 3, 88, 3, 88, 6, 88, 885, 10, 88, 13, 88, 14, 88, 886, 3, 88, 3, 88, 3, 89, 3, 89, 5, 89, 893, 10, 89, 3, 90, 3, 90, 3, 90, 3, 91, 3, 91, 3, 91, 5, 91, 901, 10, 91, 3, 91, 3, 91, 3, 91, 3, 91, 3, 91, 5, 91, 908, 10, 91, 3, 92, 3, 92, 3, 92, 5, 92, 913, 10, 92, 3, 92, 5, 92, 916, 10, 92, 3, 92, 3, 92, 3, 92, 3, 92, 6, 92, 922, 10, 92, 13, 92, 14, 92, 923, 3, 92, 3, 92, 5, 92, 928, 10, 92, 3, 93, 3, 93, 3, 94, 3, 94, 3, 94, 5, 94, 935, 10, 94, 3, 94, 3, 94, 3, 94, 5, 94, 940, 10, 94, 3, 94, 3, 94, 3, 94, 5, 94, 945, 10, 94, 3, 95, 3, 95, 3, 95, 3, 95, 3, 95, 6, 95, 952, 10, 95, 13, 95, 14, 95, 953, 3, 95, 3, 95, 3, 96, 3, 96, 3, 96, 5, 96, 961, 10, 96, 3, 97, 3, 97, 3, 98, 3, 98, 3, 99, 3, 99, 3, 99, 3, 99, 3, 100, 3, 100, 3, 100, 3, 100, 3, 100, 3, 100, 5, 100, 977, 10, 100, 3, 100, 3, 100, 3, 101, 3, 101, 3, 101, 3, 101, 3, 102, 3, 102, 3, 102, 3, 102, 3, 103, 3, 103, 3, 103, 5, 103, 992, 10, 103, 3, 103, 3, 103, 3, 104, 3, 104, 3, 104, 3, 104, 5, 104, 1000, 10, 104, 3, 104, 3, 104, 3, 104, 5, 104, 1005, 10, 104, 3, 104, 3, 104, 3, 104, 3, 105, 3, 105, 3, 106, 3, 106, 5, 106, 1014, 10, 106, 3, 106, 3, 106, 3, 106, 3, 106, 3, 106, 3, 106, 3, 106, 5, 106, 1023, 10, 106, 3, 106, 3, 106, 5, 106, 1027, 10, 106, 3, 107, 3, 107, 3, 108, 3, 108, 3, 109, 3, 109, 5, 109, 1035, 10, 109, 3, 110, 3, 110, 3, 110, 5, 110, 1040, 10, 110, 3, 110, 7, 110, 1043, 10, 110, 12, 110, 14, 110, 1046, 11, 110, 3, 110, 3, 110, 3, 110, 3, 111, 3, 111, 3, 111, 5, 111, 1054, 10, 111, 3, 111, 7, 111, 1057, 10, 111, 12, 111, 14, 111, 1060, 11, 111, 3, 111, 3, 111, 3, 111, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 3, 112, 5, 112, 1087, 10, 112, 3, 113, 3, 113, 3, 114, 3, 114, 5, 114, 1093, 10, 114, 3, 115, 3, 115, 3, 115, 3, 115, 3, 115, 3, 115, 3, 116, 3, 116, 3, 116, 7, 116, 1104, 10, 116, 12, 116, 14, 116, 1107, 11, 116, 3, 117, 3, 117, 3, 117, 7, 117, 1112, 10, 117, 12, 117, 14, 117, 1115, 11, 117, 3, 118, 3, 118, 3, 118, 7, 118, 1120, 10, 118, 12, 118, 14, 118, 1123, 11, 118, 3, 119, 3, 119, 3, 119, 5, 119, 1128, 10, 119, 3, 120, 3, 120, 3, 120, 3, 120, 3, 120, 3, 120, 3, 120, 7, 120, 1137, 10, 120, 12, 120, 14, 120, 1140, 11, 120, 3, 121, 3, 121, 3, 122, 3, 122, 3, 122, 3, 122, 3, 122, 3, 122, 3, 122, 7, 122, 1151, 10, 122, 12, 122, 14, 122, 1154, 11, 122, 3, 123, 3, 123, 3, 124, 3, 124, 3, 124, 3, 124, 3, 124, 3, 124, 3, 124, 7, 124, 1165, 10, 124, 12, 124, 14, 124, 1168, 11, 124, 3, 125, 3, 125, 3, 126, 3, 126, 3, 126, 5, 126, 1175, 10, 126, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 3, 127, 5, 127, 1202, 10, 127, 3, 127, 3, 127, 3, 127, 3, 127, 7, 127, 1208, 10, 127, 12, 127, 14, 127, 1211, 11, 127, 3, 128, 3, 128, 3, 128, 3, 128, 3, 129, 3, 129, 3, 129, 3, 129, 3, 129, 3, 129, 3, 129, 5, 129, 1224, 10, 129, 3, 130, 3, 130, 3, 130, 3, 130, 3, 130, 3, 130, 3, 130, 3, 130, 3, 130, 5, 130, 1235, 10, 130, 3, 131, 3, 131, 3, 131, 3, 131, 7, 131, 1241, 10, 131, 12, 131, 14, 131, 1244, 11, 131, 3, 131, 3, 131, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 3, 132, 5, 132, 1261, 10, 132, 3, 133, 3, 133, 3, 133, 7, 133, 1266, 10, 133, 12, 133, 14, 133, 1269, 11, 133, 3, 133, 3, 133, 3, 134, 3, 134, 3, 134, 7, 134, 1276, 10, 134, 12, 134, 14, 134, 1279, 11, 134, 3, 135, 3, 135, 3, 135, 3, 135, 3, 135, 5, 135, 1286, 10, 135, 3, 136, 3, 136, 3, 137, 3, 137, 3, 137, 7, 137, 1293, 10, 137, 12, 137, 14, 137, 1296, 11, 137, 3, 137, 3, 137, 7, 137, 1300, 10, 137, 12, 137, 14, 137, 1303, 11, 137, 3, 137, 3, 137, 3, 137, 7, 137, 1308, 10, 137, 12, 137, 14, 137, 1311, 11, 137, 5, 137, 1313, 10, 137, 3, 138, 3, 138, 3, 139, 3, 139, 3, 139, 3, 139, 3, 140, 3, 140, 5, 140, 1323, 10, 140, 3, 140, 3, 140, 3, 141, 3, 141, 3, 141, 2, 7, 10, 238, 242, 246, 252, 142, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 2, 11, 3, 2, 14, 21, 3, 2, 101, 102, 3, 2, 35, 39, 3, 2, 52, 53, 3, 2, 57, 59, 4, 2, 25, 25, 78, 83, 3, 2, 84, 85, 3, 2, 86, 88, 3, 2, 101, 103, 2, 1360, 2, 285, 3, 2, 2, 2, 4, 296, 3, 2, 2, 2, 6, 303, 3, 2, 2, 2, 8, 307, 3, 2, 2, 2, 10, 309, 3, 2, 2, 2, 12, 331, 3, 2, 2, 2, 14, 333, 3, 2, 2, 2, 16, 339, 3, 2, 2, 2, 18, 341, 3, 2, 2, 2, 20, 343, 3, 2, 2, 2, 22, 348, 3, 2, 2, 2, 24, 356, 3, 2, 2, 2, 26, 360, 3, 2, 2, 2, 28, 362, 3, 2, 2, 2, 30, 370, 3, 2, 2, 2, 32, 374, 3, 2, 2, 2, 34, 387, 3, 2, 2, 2, 36, 393, 3, 2, 2, 2, 38, 399, 3, 2, 2, 2, 40, 401, 3, 2, 2, 2, 42, 416, 3, 2, 2, 2, 44, 421, 3, 2, 2, 2, 46, 423, 3, 2, 2, 2, 48, 425, 3, 2, 2, 2, 50, 427, 3, 2, 2, 2, 52, 431, 3, 2, 2, 2, 54, 440, 3, 2, 2, 2, 56, 458, 3, 2, 2, 2, 58, 468, 3, 2, 2, 2, 60, 470, 3, 2, 2, 2, 62, 472, 3, 2, 2, 2, 64, 474, 3, 2, 2, 2, 66, 492, 3, 2, 2, 2, 68, 502, 3, 2, 2, 2, 70, 504, 3, 2, 2, 2, 72, 506, 3, 2, 2, 2, 74, 525, 3, 2, 2, 2, 76, 536, 3, 2, 2, 2, 78, 541, 3, 2, 2, 2, 80, 545, 3, 2, 2, 2, 82, 547, 3, 2, 2, 2, 84, 566, 3, 2, 2, 2, 86, 571, 3, 2, 2, 2, 88, 595, 3, 2, 2, 2, 90, 599, 3, 2, 2, 2, 92, 601, 3, 2, 2, 2, 94, 616, 3, 2, 2, 2, 96, 630, 3, 2, 2, 2, 98, 636, 3, 2, 2, 2, 100, 638, 3, 2, 2, 2, 102, 659, 3, 2, 2, 2, 104, 664, 3, 2, 2, 2, 106, 666, 3, 2, 2, 2, 108, 668, 3, 2, 2, 2, 110, 672, 3, 2, 2, 2, 112, 674, 3, 2, 2, 2, 114, 676, 3, 2, 2, 2, 116, 699, 3, 2, 2, 2, 118, 701, 3, 2, 2, 2, 120, 704, 3, 2, 2, 2, 122, 707, 3, 2, 2, 2, 124, 709, 3, 2, 2, 2, 126, 714, 3, 2, 2, 2, 128, 723, 3, 2, 2, 2, 130, 725, 3, 2, 2, 2, 132, 730, 3, 2, 2, 2, 134, 735, 3, 2, 2, 2, 136, 740, 3, 2, 2, 2, 138, 752, 3, 2, 2, 2, 140, 754, 3, 2, 2, 2, 142, 758, 3, 2, 2, 2, 144, 760, 3, 2, 2, 2, 146, 778, 3, 2, 2, 2, 148, 798, 3, 2, 2, 2, 150, 809, 3, 2, 2, 2, 152, 811, 3, 2, 2, 2, 154, 824, 3, 2, 2, 2, 156, 828, 3, 2, 2, 2, 158, 830, 3, 2, 2, 2, 160, 839, 3, 2, 2, 2, 162, 841, 3, 2, 2, 2, 164, 843, 3, 2, 2, 2, 166, 851, 3, 2, 2, 2, 168, 859, 3, 2, 2, 2, 170, 869, 3, 2, 2, 2, 172, 876, 3, 2, 2, 2, 174, 878, 3, 2, 2, 2, 176, 892, 3, 2, 2, 2, 178, 894, 3, 2, 2, 2, 180, 900, 3, 2, 2, 2, 182, 909, 3, 2, 2, 2, 184, 929, 3, 2, 2, 2, 186, 934, 3, 2, 2, 2, 188, 946, 3, 2, 2, 2, 190, 960, 3, 2, 2, 2, 192, 962, 3, 2, 2, 2, 194, 964, 3, 2, 2, 2, 196, 966, 3, 2, 2, 2, 198, 970, 3, 2, 2, 2, 200, 980, 3, 2, 2, 2, 202, 984, 3, 2, 2, 2, 204, 988, 3, 2, 2, 2, 206, 995, 3, 2, 2, 2, 208, 1009, 3, 2, 2, 2, 210, 1011, 3, 2, 2, 2, 212, 1028, 3, 2, 2, 2, 214, 1030, 3, 2, 2, 2, 216, 1034, 3, 2, 2, 2, 218, 1036, 3, 2, 2, 2, 220, 1050, 3, 2, 2, 2, 222, 1086, 3, 2, 2, 2, 224, 1088, 3, 2, 2, 2, 226, 1092, 3, 2, 2, 2, 228, 1094, 3, 2, 2, 2, 230, 1100, 3, 2, 2, 2, 232, 1108, 3, 2, 2, 2, 234, 1116, 3, 2, 2, 2, 236, 1127, 3, 2, 2, 2, 238, 1129, 3, 2, 2, 2, 240, 1141, 3, 2, 2, 2, 242, 1143, 3, 2, 2, 2, 244, 1155, 3, 2, 2, 2, 246, 1157, 3, 2, 2, 2, 248, 1169, 3, 2, 2, 2, 250, 1174, 3, 2, 2, 2, 252, 1176, 3, 2, 2, 2, 254, 1212, 3, 2, 2, 2, 256, 1223, 3, 2, 2, 2, 258, 1234, 3, 2, 2, 2, 260, 1236, 3, 2, 2, 2, 262, 1260, 3, 2, 2, 2, 264, 1267, 3, 2, 2, 2, 266, 1272, 3, 2, 2, 2, 268, 1280, 3, 2, 2, 2, 270, 1287, 3, 2, 2, 2, 272, 1312, 3, 2, 2, 2, 274, 1314, 3, 2, 2, 2, 276, 1316, 3, 2, 2, 2, 278, 1322, 3, 2, 2, 2, 280, 1326, 3, 2, 2, 2, 282, 284, 5, 4, 3, 2, 283, 282, 3, 2, 2, 2, 284, 287, 3, 2, 2, 2, 285, 283, 3, 2, 2, 2, 285, 286, 3, 2, 2, 2, 286, 291, 3, 2, 2, 2, 287, 285, 3, 2, 2, 2, 288, 290, 5, 12, 7, 2, 289, 288, 3, 2, 2, 2, 290, 293, 3, 2, 2, 2, 291, 289, 3, 2, 2, 2, 291, 292, 3, 2, 2, 2, 292, 294, 3, 2, 2, 2, 293, 291, 3, 2, 2, 2, 294, 295, 7, 2, 2, 3, 295, 3, 3, 2, 2, 2, 296, 297, 5, 6, 4, 2, 297, 5, 3, 2, 2, 2, 298, 299, 7, 3, 2, 2, 299, 300, 5, 8, 5, 2, 300, 301, 7, 91, 2, 2, 301, 304, 3, 2, 2, 2, 302, 304, 7, 91, 2, 2, 303, 298, 3, 2, 2, 2, 303, 302, 3, 2, 2, 2, 304, 7, 3, 2, 2, 2, 305, 308, 7, 99, 2, 2, 306, 308, 5, 10, 6, 2, 307, 305, 3, 2, 2, 2, 307, 306, 3, 2, 2, 2, 308, 9, 3, 2, 2, 2, 309, 310, 8, 6, 1, 2, 310, 311, 7, 105, 2, 2, 311, 317, 3, 2, 2, 2, 312, 313, 12, 3, 2, 2, 313, 314, 7, 4, 2, 2, 314, 316, 7, 105, 2, 2, 315, 312, 3, 2, 2, 2, 316, 319, 3, 2, 2, 2, 317, 315, 3, 2, 2, 2, 317, 318, 3, 2, 2, 2, 318, 11, 3, 2, 2, 2, 319, 317, 3, 2, 2, 2, 320, 332, 5, 14, 8, 2, 321, 332, 5, 22, 12, 2, 322, 332, 5, 40, 21, 2, 323, 332, 5, 54, 28, 2, 324, 332, 5, 64, 33, 2, 325, 332, 5, 82, 42, 2, 326, 332, 5, 72, 37, 2, 327, 332, 5, 86, 44, 2, 328, 332, 5, 90, 46, 2, 329, 332, 5, 100, 51, 2, 330, 332, 7, 91, 2, 2, 331, 320, 3, 2, 2, 2, 331, 321, 3, 2, 2, 2, 331, 322, 3, 2, 2, 2, 331, 323, 3, 2, 2, 2, 331, 324, 3, 2, 2, 2, 331, 325, 3, 2, 2, 2, 331, 326, 3, 2, 2, 2, 331, 327, 3, 2, 2, 2, 331, 328, 3, 2, 2, 2, 331, 329, 3, 2, 2, 2, 331, 330, 3, 2, 2, 2, 332, 13, 3, 2, 2, 2, 333, 334, 7, 5, 2, 2, 334, 335, 5, 16, 9, 2, 335, 336, 7, 6, 2, 2, 336, 337, 5, 18, 10, 2, 337, 338, 7, 91, 2, 2, 338, 15, 3, 2, 2, 2, 339, 340, 7, 105, 2, 2, 340, 17, 3, 2, 2, 2, 341, 342, 5, 20, 11, 2, 342, 19, 3, 2, 2, 2, 343, 344, 7, 7, 2, 2, 344, 345, 7, 94, 2, 2, 345, 346, 5, 28, 15, 2, 346, 347, 7, 95, 2, 2, 347, 21, 3, 2, 2, 2, 348, 349, 7, 8, 2, 2, 349, 350, 5, 26, 14, 2, 350, 351, 7, 9, 2, 2, 351, 352, 5, 16, 9, 2, 352, 353, 7, 6, 2, 2, 353, 354, 5, 24, 13, 2, 354, 355, 7, 91, 2, 2, 355, 23, 3, 2, 2, 2, 356, 357, 5, 32, 17, 2, 357, 25, 3, 2, 2, 2, 358, 361, 7, 105, 2, 2, 359, 361, 5, 38, 20, 2, 360, 358, 3, 2, 2, 2, 360, 359, 3, 2, 2, 2, 361, 27, 3, 2, 2, 2, 362, 367, 5, 30, 16, 2, 363, 364, 7, 10, 2, 2, 364, 366, 5, 30, 16, 2, 365, 363, 3, 2, 2, 2, 366, 369, 3, 2, 2, 2, 367, 365, 3, 2, 2, 2, 367, 368, 3, 2, 2, 2, 368, 29, 3, 2, 2, 2, 369, 367, 3, 2, 2, 2, 370, 371, 5, 38, 20, 2, 371, 372, 7, 11, 2, 2, 372, 373, 5, 280, 141, 2, 373, 31, 3, 2, 2, 2, 374, 375, 7, 7, 2, 2, 375, 376, 7, 94, 2, 2, 376, 379, 5, 28, 15, 2, 377, 378, 7, 10, 2, 2, 378, 380, 5, 34, 18, 2, 379, 377, 3, 2, 2, 2, 379, 380, 3, 2, 2, 2, 380, 383, 3, 2, 2, 2, 381, 382, 7, 10, 2, 2, 382, 384, 5, 36, 19, 2, 383, 381, 3, 2, 2, 2, 383, 384, 3, 2, 2, 2, 384, 385, 3, 2, 2, 2, 385, 386, 7, 95, 2, 2, 386, 33, 3, 2, 2, 2, 387, 388, 7, 12, 2, 2, 388, 391, 7, 11, 2, 2, 389, 392, 7, 100, 2, 2, 390, 392, 5, 280, 141, 2, 391, 389, 3, 2, 2, 2, 391, 390, 3, 2, 2, 2, 392, 35, 3, 2, 2, 2, 393, 394, 7, 13, 2, 2, 394, 397, 7, 11, 2, 2, 395, 398, 7, 100, 2, 2, 396, 398, 5, 280, 141, 2, 397, 395, 3, 2, 2, 2, 397, 396, 3, 2, 2, 2, 398, 37, 3, 2, 2, 2, 399, 400, 9, 2, 2, 2, 400, 39, 3, 2, 2, 2, 401, 402, 7, 22, 2, 2, 402, 403, 5, 46, 24, 2, 403, 404, 7, 11, 2, 2, 404, 405, 7, 92, 2, 2, 405, 410, 5, 42, 22, 2, 406, 407, 7, 10, 2, 2, 407, 409, 5, 42, 22, 2, 408, 406, 3, 2, 2, 2, 409, 412, 3, 2, 2, 2, 410, 408, 3, 2, 2, 2, 410, 411, 3, 2, 2, 2, 411, 413, 3, 2, 2, 2, 412, 410, 3, 2, 2, 2, 413, 414, 7, 93, 2, 2, 414, 415, 7, 91, 2, 2, 415, 41, 3, 2, 2, 2, 416, 419, 5, 48, 25, 2, 417, 418, 7, 23, 2, 2, 418, 420, 5, 44, 23, 2, 419, 417, 3, 2, 2, 2, 419, 420, 3, 2, 2, 2, 420, 43, 3, 2, 2, 2, 421, 422, 9, 3, 2, 2, 422, 45, 3, 2, 2, 2, 423, 424, 7, 105, 2, 2, 424, 47, 3, 2, 2, 2, 425, 426, 7, 105, 2, 2, 426, 49, 3, 2, 2, 2, 427, 428, 5, 46, 24, 2, 428, 429, 7, 24, 2, 2, 429, 430, 5, 48, 25, 2, 430, 51, 3, 2, 2, 2, 431, 432, 7, 94, 2, 2, 432, 433, 5, 60, 31, 2, 433, 436, 7, 25, 2, 2, 434, 437, 5, 50, 26, 2, 435, 437, 7, 104, 2, 2, 436, 434, 3, 2, 2, 2, 436, 435, 3, 2, 2, 2, 437, 438, 3, 2, 2, 2, 438, 439, 7, 95, 2, 2, 439, 53, 3, 2, 2, 2, 440, 441, 7, 26, 2, 2, 441, 443, 5, 62, 32, 2, 442, 444, 5, 56, 29, 2, 443, 442, 3, 2, 2, 2, 443, 444, 3, 2, 2, 2, 444, 456, 3, 2, 2, 2, 445, 446, 7, 11, 2, 2, 446, 447, 7, 91, 2, 2, 447, 449, 7, 106, 2, 2, 448, 450, 5, 58, 30, 2, 449, 448, 3, 2, 2, 2, 450, 451, 3, 2, 2, 2, 451, 449, 3, 2, 2, 2, 451, 452, 3, 2, 2, 2, 452, 453, 3, 2, 2, 2, 453, 454, 7, 107, 2, 2, 454, 457, 3, 2, 2, 2, 455, 457, 7, 91, 2, 2, 456, 445, 3, 2, 2, 2, 456, 455, 3, 2, 2, 2, 457, 55, 3, 2, 2, 2, 458, 459, 7, 27, 2, 2, 459, 461, 5, 62, 32, 2, 460, 462, 5, 52, 27, 2, 461, 460, 3, 2, 2, 2, 461, 462, 3, 2, 2, 2, 462, 57, 3, 2, 2, 2, 463, 469, 5, 114, 58, 2, 464, 469, 5, 142, 72, 2, 465, 469, 5, 156, 79, 2, 466, 469, 5, 206, 104, 2, 467, 469, 5, 216, 109, 2, 468, 463, 3, 2, 2, 2, 468, 464, 3, 2, 2, 2, 468, 465, 3, 2, 2, 2, 468, 466, 3, 2, 2, 2, 468, 467, 3, 2, 2, 2, 469, 59, 3, 2, 2, 2, 470, 471, 7, 105, 2, 2, 471, 61, 3, 2, 2, 2, 472, 473, 7, 105, 2, 2, 473, 63, 3, 2, 2, 2, 474, 475, 7, 28, 2, 2, 475, 477, 5, 70, 36, 2, 476, 478, 5, 66, 34, 2, 477, 476, 3, 2, 2, 2, 477, 478, 3, 2, 2, 2, 478, 490, 3, 2, 2, 2, 479, 480, 7, 11, 2, 2, 480, 481, 7, 91, 2, 2, 481, 483, 7, 106, 2, 2, 482, 484, 5, 68, 35, 2, 483, 482, 3, 2, 2, 2, 484, 485, 3, 2, 2, 2, 485, 483, 3, 2, 2, 2, 485, 486, 3, 2, 2, 2, 486, 487, 3, 2, 2, 2, 487, 488, 7, 107, 2, 2, 488, 491, 3, 2, 2, 2, 489, 491, 7, 91, 2, 2, 490, 479, 3, 2, 2, 2, 490, 489, 3, 2, 2, 2, 491, 65, 3, 2, 2, 2, 492, 493, 7, 27, 2, 2, 493, 495, 5, 70, 36, 2, 494, 496, 5, 52, 27, 2, 495, 494, 3, 2, 2, 2, 495, 496, 3, 2, 2, 2, 496, 67, 3, 2, 2, 2, 497, 503, 5, 114, 58, 2, 498, 503, 5, 142, 72, 2, 499, 503, 5, 156, 79, 2, 500, 503, 5, 206, 104, 2, 501, 503, 5, 216, 109, 2, 502, 497, 3, 2, 2, 2, 502, 498, 3, 2, 2, 2, 502, 499, 3, 2, 2, 2, 502, 500, 3, 2, 2, 2, 502, 501, 3, 2, 2, 2, 503, 69, 3, 2, 2, 2, 504, 505, 7, 105, 2, 2, 505, 71, 3, 2, 2, 2, 506, 507, 7, 29, 2, 2, 507, 509, 5, 78, 40, 2, 508, 510, 5, 74, 38, 2, 509, 508, 3, 2, 2, 2, 509, 510, 3, 2, 2, 2, 510, 523, 3, 2, 2, 2, 511, 512, 7, 11, 2, 2, 512, 513, 7, 91, 2, 2, 513, 516, 7, 106, 2, 2, 514, 517, 5, 76, 39, 2, 515, 517, 5, 172, 87, 2, 516, 514, 3, 2, 2, 2, 516, 515, 3, 2, 2, 2, 517, 518, 3, 2, 2, 2, 518, 516, 3, 2, 2, 2, 518, 519, 3, 2, 2, 2, 519, 520, 3, 2, 2, 2, 520, 521, 7, 107, 2, 2, 521, 524, 3, 2, 2, 2, 522, 524, 7, 91, 2, 2, 523, 511, 3, 2, 2, 2, 523, 522, 3, 2, 2, 2, 524, 73, 3, 2, 2, 2, 525, 526, 7, 27, 2, 2, 526, 528, 5, 78, 40, 2, 527, 529, 5, 52, 27, 2, 528, 527, 3, 2, 2, 2, 528, 529, 3, 2, 2, 2, 529, 75, 3, 2, 2, 2, 530, 537, 5, 114, 58, 2, 531, 537, 5, 142, 72, 2, 532, 537, 5, 156, 79, 2, 533, 537, 5, 206, 104, 2, 534, 537, 5, 216, 109, 2, 535, 537, 5, 168, 85, 2, 536, 530, 3, 2, 2, 2, 536, 531, 3, 2, 2, 2, 536, 532, 3, 2, 2, 2, 536, 533, 3, 2, 2, 2, 536, 534, 3, 2, 2, 2, 536, 535, 3, 2, 2, 2, 537, 77, 3, 2, 2, 2, 538, 539, 5, 70, 36, 2, 539, 540, 7, 4, 2, 2, 540, 542, 3, 2, 2, 2, 541, 538, 3, 2, 2, 2, 541, 542, 3, 2, 2, 2, 542, 543, 3, 2, 2, 2, 543, 544, 5, 80, 41, 2, 544, 79, 3, 2, 2, 2, 545, 546, 7, 105, 2, 2, 546, 81, 3, 2, 2, 2, 547, 548, 7, 30, 2, 2, 548, 550, 5, 78, 40, 2, 549, 551, 5, 84, 43, 2, 550, 549, 3, 2, 2, 2, 550, 551, 3, 2, 2, 2, 551, 564, 3, 2, 2, 2, 552, 553, 7, 11, 2, 2, 553, 554, 7, 91, 2, 2, 554, 557, 7, 106, 2, 2, 555, 558, 5, 76, 39, 2, 556, 558, 5, 172, 87, 2, 557, 555, 3, 2, 2, 2, 557, 556, 3, 2, 2, 2, 558, 559, 3, 2, 2, 2, 559, 557, 3, 2, 2, 2, 559, 560, 3, 2, 2, 2, 560, 561, 3, 2, 2, 2, 561, 562, 7, 107, 2, 2, 562, 565, 3, 2, 2, 2, 563, 565, 7, 91, 2, 2, 564, 552, 3, 2, 2, 2, 564, 563, 3, 2, 2, 2, 565, 83, 3, 2, 2, 2, 566, 567, 7, 27, 2, 2, 567, 569, 5, 78, 40, 2, 568, 570, 5, 52, 27, 2, 569, 568, 3, 2, 2, 2, 569, 570, 3, 2, 2, 2, 570, 85, 3, 2, 2, 2, 571, 575, 7, 31, 2, 2, 572, 573, 5, 70, 36, 2, 573, 574, 7, 4, 2, 2, 574, 576, 3, 2, 2, 2, 575, 572, 3, 2, 2, 2, 575, 576, 3, 2, 2, 2, 576, 577, 3, 2, 2, 2, 577, 580, 5, 88, 45, 2, 578, 579, 7, 9, 2, 2, 579, 581, 5, 78, 40, 2, 580, 578, 3, 2, 2, 2, 580, 581, 3, 2, 2, 2, 581, 593, 3, 2, 2, 2, 582, 583, 7, 11, 2, 2, 583, 584, 7, 91, 2, 2, 584, 586, 7, 106, 2, 2, 585, 587, 5, 76, 39, 2, 586, 585, 3, 2, 2, 2, 587, 588, 3, 2, 2, 2, 588, 586, 3, 2, 2, 2, 588, 589, 3, 2, 2, 2, 589, 590, 3, 2, 2, 2, 590, 591, 7, 107, 2, 2, 591, 594, 3, 2, 2, 2, 592, 594, 7, 91, 2, 2, 593, 582, 3, 2, 2, 2, 593, 592, 3, 2, 2, 2, 594, 87, 3, 2, 2, 2, 595, 596, 7, 105, 2, 2, 596, 89, 3, 2, 2, 2, 597, 600, 5, 92, 47, 2, 598, 600, 5, 94, 48, 2, 599, 597, 3, 2, 2, 2, 599, 598, 3, 2, 2, 2, 600, 91, 3, 2, 2, 2, 601, 602, 7, 32, 2, 2, 602, 603, 5, 46, 24, 2, 603, 604, 7, 11, 2, 2, 604, 605, 7, 92, 2, 2, 605, 610, 5, 42, 22, 2, 606, 607, 7, 10, 2, 2, 607, 609, 5, 42, 22, 2, 608, 606, 3, 2, 2, 2, 609, 612, 3, 2, 2, 2, 610, 608, 3, 2, 2, 2, 610, 611, 3, 2, 2, 2, 611, 613, 3, 2, 2, 2, 612, 610, 3, 2, 2, 2, 613, 614, 7, 93, 2, 2, 614, 615, 7, 91, 2, 2, 615, 93, 3, 2, 2, 2, 616, 617, 7, 32, 2, 2, 617, 618, 5, 96, 49, 2, 618, 619, 7, 11, 2, 2, 619, 620, 7, 91, 2, 2, 620, 622, 7, 106, 2, 2, 621, 623, 5, 98, 50, 2, 622, 621, 3, 2, 2, 2, 623, 624, 3, 2, 2, 2, 624, 622, 3, 2, 2, 2, 624, 625, 3, 2, 2, 2, 625, 626, 3, 2, 2, 2, 626, 627, 7, 107, 2, 2, 627, 95, 3, 2, 2, 2, 628, 631, 5, 112, 57, 2, 629, 631, 5, 78, 40, 2, 630, 628, 3, 2, 2, 2, 630, 629, 3, 2, 2, 2, 631, 97, 3, 2, 2, 2, 632, 637, 5, 58, 30, 2, 633, 637, 5, 68, 35, 2, 634, 637, 5, 76, 39, 2, 635, 637, 5, 172, 87, 2, 636, 632, 3, 2, 2, 2, 636, 633, 3, 2, 2, 2, 636, 634, 3, 2, 2, 2, 636, 635, 3, 2, 2, 2, 637, 99, 3, 2, 2, 2, 638, 639, 7, 33, 2, 2, 639, 644, 5, 60, 31, 2, 640, 641, 7, 10, 2, 2, 641, 643, 5, 60, 31, 2, 642, 640, 3, 2, 2, 2, 643, 646, 3, 2, 2, 2, 644, 642, 3, 2, 2, 2, 644, 645, 3, 2, 2, 2, 645, 647, 3, 2, 2, 2, 646, 644, 3, 2, 2, 2, 647, 648, 7, 11, 2, 2, 648, 651, 5, 102, 52, 2, 649, 650, 7, 23, 2, 2, 650, 652, 5, 150, 76, 2, 651, 649, 3, 2, 2, 2, 651, 652, 3, 2, 2, 2, 652, 655, 3, 2, 2, 2, 653, 656, 5, 152, 77, 2, 654, 656, 7, 91, 2, 2, 655, 653, 3, 2, 2, 2, 655, 654, 3, 2, 2, 2, 656, 101, 3, 2, 2, 2, 657, 660, 5, 104, 53, 2, 658, 660, 5, 106, 54, 2, 659, 657, 3, 2, 2, 2, 659, 658, 3, 2, 2, 2, 660, 103, 3, 2, 2, 2, 661, 665, 5, 110, 56, 2, 662, 665, 5, 112, 57, 2, 663, 665, 5, 78, 40, 2, 664, 661, 3, 2, 2, 2, 664, 662, 3, 2, 2, 2, 664, 663, 3, 2, 2, 2, 665, 105, 3, 2, 2, 2, 666, 667, 5, 108, 55, 2, 667, 107, 3, 2, 2, 2, 668, 669, 7, 34, 2, 2, 669, 670, 7, 9, 2, 2, 670, 671, 5, 104, 53, 2, 671, 109, 3, 2, 2, 2, 672, 673, 9, 4, 2, 2, 673, 111, 3, 2, 2, 2, 674, 675, 7, 105, 2, 2, 675, 113, 3, 2, 2, 2, 676, 677, 7, 40, 2, 2, 677, 682, 5, 124, 63, 2, 678, 679, 7, 94, 2, 2, 679, 680, 5, 266, 134, 2, 680, 681, 7, 95, 2, 2, 681, 683, 3, 2, 2, 2, 682, 678, 3, 2, 2, 2, 682, 683, 3, 2, 2, 2, 683, 686, 3, 2, 2, 2, 684, 685, 7, 6, 2, 2, 685, 687, 5, 116, 59, 2, 686, 684, 3, 2, 2, 2, 686, 687, 3, 2, 2, 2, 687, 688, 3, 2, 2, 2, 688, 689, 7, 91, 2, 2, 689, 115, 3, 2, 2, 2, 690, 696, 5, 118, 60, 2, 691, 693, 5, 120, 61, 2, 692, 691, 3, 2, 2, 2, 692, 693, 3, 2, 2, 2, 693, 694, 3, 2, 2, 2, 694, 695, 7, 41, 2, 2, 695, 697, 5, 128, 65, 2, 696, 692, 3, 2, 2, 2, 696, 697, 3, 2, 2, 2, 697, 700, 3, 2, 2, 2, 698, 700, 5, 128, 65, 2, 699, 690, 3, 2, 2, 2, 699, 698, 3, 2, 2, 2, 700, 117, 3, 2, 2, 2, 701, 702, 7, 42, 2, 2, 702, 703, 5, 126, 64, 2, 703, 119, 3, 2, 2, 2, 704, 705, 7, 43, 2, 2, 705, 706, 5, 122, 62, 2, 706, 121, 3, 2, 2, 2, 707, 708, 7, 105, 2, 2, 708, 123, 3, 2, 2, 2, 709, 710, 7, 105, 2, 2, 710, 125, 3, 2, 2, 2, 711, 712, 5, 226, 114, 2, 712, 713, 7, 4, 2, 2, 713, 715, 3, 2, 2, 2, 714, 711, 3, 2, 2, 2, 714, 715, 3, 2, 2, 2, 715, 716, 3, 2, 2, 2, 716, 717, 5, 124, 63, 2, 717, 127, 3, 2, 2, 2, 718, 724, 5, 138, 70, 2, 719, 724, 5, 130, 66, 2, 720, 724, 5, 132, 67, 2, 721, 724, 5, 134, 68, 2, 722, 724, 5, 136, 69, 2, 723, 718, 3, 2, 2, 2, 723, 719, 3, 2, 2, 2, 723, 720, 3, 2, 2, 2, 723, 721, 3, 2, 2, 2, 723, 722, 3, 2, 2, 2, 724, 129, 3, 2, 2, 2, 725, 726, 7, 44, 2, 2, 726, 727, 7, 94, 2, 2, 727, 728, 5, 138, 70, 2, 728, 729, 7, 95, 2, 2, 729, 131, 3, 2, 2, 2, 730, 731, 7, 45, 2, 2, 731, 732, 7, 94, 2, 2, 732, 733, 5, 138, 70, 2, 733, 734, 7, 95, 2, 2, 734, 133, 3, 2, 2, 2, 735, 736, 7, 46, 2, 2, 736, 737, 7, 94, 2, 2, 737, 738, 5, 140, 71, 2, 738, 739, 7, 95, 2, 2, 739, 135, 3, 2, 2, 2, 740, 741, 7, 47, 2, 2, 741, 742, 7, 94, 2, 2, 742, 748, 5, 140, 71, 2, 743, 744, 7, 10, 2, 2, 744, 745, 7, 105, 2, 2, 745, 746, 8, 69, 1, 2, 746, 747, 7, 11, 2, 2, 747, 749, 5, 140, 71, 2, 748, 743, 3, 2, 2, 2, 748, 749, 3, 2, 2, 2, 749, 750, 3, 2, 2, 2, 750, 751, 7, 95, 2, 2, 751, 137, 3, 2, 2, 2, 752, 753, 5, 226, 114, 2, 753, 139, 3, 2, 2, 2, 754, 755, 5, 226, 114, 2, 755, 141, 3, 2, 2, 2, 756, 759, 5, 144, 73, 2, 757, 759, 5, 146, 74, 2, 758, 756, 3, 2, 2, 2, 758, 757, 3, 2, 2, 2, 759, 143, 3, 2, 2, 2, 760, 765, 5, 60, 31, 2, 761, 762, 7, 10, 2, 2, 762, 764, 5, 60, 31, 2, 763, 761, 3, 2, 2, 2, 764, 767, 3, 2, 2, 2, 765, 763, 3, 2, 2, 2, 765, 766, 3, 2, 2, 2, 766, 768, 3, 2, 2, 2, 767, 765, 3, 2, 2, 2, 768, 769, 7, 11, 2, 2, 769, 772, 5, 102, 52, 2, 770, 771, 7, 23, 2, 2, 771, 773, 5, 150, 76, 2, 772, 770, 3, 2, 2, 2, 772, 773, 3, 2, 2, 2, 773, 776, 3, 2, 2, 2, 774, 777, 5, 152, 77, 2, 775, 777, 7, 91, 2, 2, 776, 774, 3, 2, 2, 2, 776, 775, 3, 2, 2, 2, 777, 145, 3, 2, 2, 2, 778, 779, 7, 48, 2, 2, 779, 784, 5, 60, 31, 2, 780, 781, 7, 10, 2, 2, 781, 783, 5, 60, 31, 2, 782, 780, 3, 2, 2, 2, 783, 786, 3, 2, 2, 2, 784, 782, 3, 2, 2, 2, 784, 785, 3, 2, 2, 2, 785, 787, 3, 2, 2, 2, 786, 784, 3, 2, 2, 2, 787, 788, 7, 11, 2, 2, 788, 794, 5, 102, 52, 2, 789, 792, 7, 23, 2, 2, 790, 793, 5, 148, 75, 2, 791, 793, 5, 258, 130, 2, 792, 790, 3, 2, 2, 2, 792, 791, 3, 2, 2, 2, 793, 795, 3, 2, 2, 2, 794, 789, 3, 2, 2, 2, 794, 795, 3, 2, 2, 2, 795, 796, 3, 2, 2, 2, 796, 797, 7, 91, 2, 2, 797, 147, 3, 2, 2, 2, 798, 799, 7, 49, 2, 2, 799, 800, 7, 94, 2, 2, 800, 801, 5, 226, 114, 2, 801, 802, 7, 10, 2, 2, 802, 805, 5, 116, 59, 2, 803, 804, 7, 10, 2, 2, 804, 806, 5, 150, 76, 2, 805, 803, 3, 2, 2, 2, 805, 806, 3, 2, 2, 2, 806, 807, 3, 2, 2, 2, 807, 808, 7, 95, 2, 2, 808, 149, 3, 2, 2, 2, 809, 810, 5, 226, 114, 2, 810, 151, 3, 2, 2, 2, 811, 812, 7, 50, 2, 2, 812, 813, 7, 11, 2, 2, 813, 814, 7, 91, 2, 2, 814, 816, 7, 106, 2, 2, 815, 817, 5, 154, 78, 2, 816, 815, 3, 2, 2, 2, 817, 818, 3, 2, 2, 2, 818, 816, 3, 2, 2, 2, 818, 819, 3, 2, 2, 2, 819, 820, 3, 2, 2, 2, 820, 821, 7, 107, 2, 2, 821, 153, 3, 2, 2, 2, 822, 825, 5, 156, 79, 2, 823, 825, 5, 216, 109, 2, 824, 822, 3, 2, 2, 2, 824, 823, 3, 2, 2, 2, 825, 155, 3, 2, 2, 2, 826, 829, 5, 158, 80, 2, 827, 829, 5, 164, 83, 2, 828, 826, 3, 2, 2, 2, 828, 827, 3, 2, 2, 2, 829, 157, 3, 2, 2, 2, 830, 831, 7, 51, 2, 2, 831, 833, 7, 94, 2, 2, 832, 834, 5, 160, 81, 2, 833, 832, 3, 2, 2, 2, 833, 834, 3, 2, 2, 2, 834, 835, 3, 2, 2, 2, 835, 836, 5, 162, 82, 2, 836, 837, 7, 95, 2, 2, 837, 838, 7, 91, 2, 2, 838, 159, 3, 2, 2, 2, 839, 840, 9, 5, 2, 2, 840, 161, 3, 2, 2, 2, 841, 842, 5, 226, 114, 2, 842, 163, 3, 2, 2, 2, 843, 844, 7, 54, 2, 2, 844, 845, 7, 94, 2, 2, 845, 846, 5, 166, 84, 2, 846, 847, 7, 95, 2, 2, 847, 848, 7, 91, 2, 2, 848, 165, 3, 2, 2, 2, 849, 852, 5, 60, 31, 2, 850, 852, 5, 254, 128, 2, 851, 849, 3, 2, 2, 2, 851, 850, 3, 2, 2, 2, 852, 167, 3, 2, 2, 2, 853, 856, 5, 170, 86, 2, 854, 856, 5, 194, 98, 2, 855, 853, 3, 2, 2, 2, 855, 854, 3, 2, 2, 2, 856, 857, 3, 2, 2, 2, 857, 858, 7, 4, 2, 2, 858, 860, 3, 2, 2, 2, 859, 855, 3, 2, 2, 2, 859, 860, 3, 2, 2, 2, 860, 861, 3, 2, 2, 2, 861, 862, 5, 88, 45, 2, 862, 864, 7, 94, 2, 2, 863, 865, 5, 272, 137, 2, 864, 863, 3, 2, 2, 2, 864, 865, 3, 2, 2, 2, 865, 866, 3, 2, 2, 2, 866, 867, 7, 95, 2, 2, 867, 868, 7, 91, 2, 2, 868, 169, 3, 2, 2, 2, 869, 870, 5, 194, 98, 2, 870, 871, 7, 4, 2, 2, 871, 872, 3, 2, 2, 2, 872, 873, 5, 80, 41, 2, 873, 171, 3, 2, 2, 2, 874, 877, 5, 174, 88, 2, 875, 877, 5, 178, 90, 2, 876, 874, 3, 2, 2, 2, 876, 875, 3, 2, 2, 2, 877, 173, 3, 2, 2, 2, 878, 879, 7, 55, 2, 2, 879, 880, 5, 116, 59, 2, 880, 881, 7, 11, 2, 2, 881, 882, 7, 91, 2, 2, 882, 884, 7, 106, 2, 2, 883, 885, 5, 176, 89, 2, 884, 883, 3, 2, 2, 2, 885, 886, 3, 2, 2, 2, 886, 884, 3, 2, 2, 2, 886, 887, 3, 2, 2, 2, 887, 888, 3, 2, 2, 2, 888, 889, 7, 107, 2, 2, 889, 175, 3, 2, 2, 2, 890, 893, 5, 200, 101, 2, 891, 893, 5, 198, 100, 2, 892, 890, 3, 2, 2, 2, 892, 891, 3, 2, 2, 2, 893, 177, 3, 2, 2, 2, 894, 895, 7, 56, 2, 2, 895, 896, 5, 180, 91, 2, 896, 179, 3, 2, 2, 2, 897, 898, 5, 192, 97, 2, 898, 899, 7, 11, 2, 2, 899, 901, 3, 2, 2, 2, 900, 897, 3, 2, 2, 2, 900, 901, 3, 2, 2, 2, 901, 907, 3, 2, 2, 2, 902, 908, 5, 182, 92, 2, 903, 908, 5, 186, 94, 2, 904, 908, 5, 196, 99, 2, 905, 908, 5, 198, 100, 2, 906, 908, 5, 200, 101, 2, 907, 902, 3, 2, 2, 2, 907, 903, 3, 2, 2, 2, 907, 904, 3, 2, 2, 2, 907, 905, 3, 2, 2, 2, 907, 906, 3, 2, 2, 2, 908, 181, 3, 2, 2, 2, 909, 915, 5, 184, 93, 2, 910, 912, 7, 94, 2, 2, 911, 913, 5, 272, 137, 2, 912, 911, 3, 2, 2, 2, 912, 913, 3, 2, 2, 2, 913, 914, 3, 2, 2, 2, 914, 916, 7, 95, 2, 2, 915, 910, 3, 2, 2, 2, 915, 916, 3, 2, 2, 2, 916, 917, 3, 2, 2, 2, 917, 918, 7, 11, 2, 2, 918, 919, 7, 91, 2, 2, 919, 921, 7, 106, 2, 2, 920, 922, 5, 180, 91, 2, 921, 920, 3, 2, 2, 2, 922, 923, 3, 2, 2, 2, 923, 921, 3, 2, 2, 2, 923, 924, 3, 2, 2, 2, 924, 925, 3, 2, 2, 2, 925, 927, 7, 107, 2, 2, 926, 928, 5, 188, 95, 2, 927, 926, 3, 2, 2, 2, 927, 928, 3, 2, 2, 2, 928, 183, 3, 2, 2, 2, 929, 930, 9, 6, 2, 2, 930, 185, 3, 2, 2, 2, 931, 932, 5, 194, 98, 2, 932, 933, 7, 4, 2, 2, 933, 935, 3, 2, 2, 2, 934, 931, 3, 2, 2, 2, 934, 935, 3, 2, 2, 2, 935, 936, 3, 2, 2, 2, 936, 937, 5, 80, 41, 2, 937, 939, 7, 94, 2, 2, 938, 940, 5, 272, 137, 2, 939, 938, 3, 2, 2, 2, 939, 940, 3, 2, 2, 2, 940, 941, 3, 2, 2, 2, 941, 944, 7, 95, 2, 2, 942, 945, 5, 188, 95, 2, 943, 945, 7, 91, 2, 2, 944, 942, 3, 2, 2, 2, 944, 943, 3, 2, 2, 2, 945, 187, 3, 2, 2, 2, 946, 947, 7, 50, 2, 2, 947, 948, 7, 11, 2, 2, 948, 949, 7, 91, 2, 2, 949, 951, 7, 106, 2, 2, 950, 952, 5, 190, 96, 2, 951, 950, 3, 2, 2, 2, 952, 953, 3, 2, 2, 2, 953, 951, 3, 2, 2, 2, 953, 954, 3, 2, 2, 2, 954, 955, 3, 2, 2, 2, 955, 956, 7, 107, 2, 2, 956, 189, 3, 2, 2, 2, 957, 961, 5, 156, 79, 2, 958, 961, 5, 168, 85, 2, 959, 961, 5, 202, 102, 2, 960, 957, 3, 2, 2, 2, 960, 958, 3, 2, 2, 2, 960, 959, 3, 2, 2, 2, 961, 191, 3, 2, 2, 2, 962, 963, 7, 105, 2, 2, 963, 193, 3, 2, 2, 2, 964, 965, 5, 70, 36, 2, 965, 195, 3, 2, 2, 2, 966, 967, 7, 60, 2, 2, 967, 968, 5, 116, 59, 2, 968, 969, 7, 91, 2, 2, 969, 197, 3, 2, 2, 2, 970, 971, 7, 61, 2, 2, 971, 976, 5, 124, 63, 2, 972, 973, 7, 94, 2, 2, 973, 974, 5, 272, 137, 2, 974, 975, 7, 95, 2, 2, 975, 977, 3, 2, 2, 2, 976, 972, 3, 2, 2, 2, 976, 977, 3, 2, 2, 2, 977, 978, 3, 2, 2, 2, 978, 979, 7, 91, 2, 2, 979, 199, 3, 2, 2, 2, 980, 981, 7, 62, 2, 2, 981, 982, 5, 204, 103, 2, 982, 983, 7, 91, 2, 2, 983, 201, 3, 2, 2, 2, 984, 985, 7, 63, 2, 2, 985, 986, 5, 116, 59, 2, 986, 987, 7, 91, 2, 2, 987, 203, 3, 2, 2, 2, 988, 989, 5, 252, 127, 2, 989, 991, 7, 94, 2, 2, 990, 992, 5, 272, 137, 2, 991, 990, 3, 2, 2, 2, 991, 992, 3, 2, 2, 2, 992, 993, 3, 2, 2, 2, 993, 994, 7, 95, 2, 2, 994, 205, 3, 2, 2, 2, 995, 996, 7, 64, 2, 2, 996, 997, 5, 214, 108, 2, 997, 999, 7, 94, 2, 2, 998, 1000, 5, 266, 134, 2, 999, 998, 3, 2, 2, 2, 999, 1000, 3, 2, 2, 2, 1000, 1001, 3, 2, 2, 2, 1001, 1004, 7, 95, 2, 2, 1002, 1003, 7, 65, 2, 2, 1003, 1005, 5, 208, 105, 2, 1004, 1002, 3, 2, 2, 2, 1004, 1005, 3, 2, 2, 2, 1005, 1006, 3, 2, 2, 2, 1006, 1007, 5, 210, 106, 2, 1007, 1008, 7, 91, 2, 2, 1008, 207, 3, 2, 2, 2, 1009, 1010, 5, 102, 52, 2, 1010, 209, 3, 2, 2, 2, 1011, 1013, 7, 6, 2, 2, 1012, 1014, 5, 212, 107, 2, 1013, 1012, 3, 2, 2, 2, 1013, 1014, 3, 2, 2, 2, 1014, 1026, 3, 2, 2, 2, 1015, 1016, 7, 66, 2, 2, 1016, 1027, 5, 226, 114, 2, 1017, 1027, 7, 67, 2, 2, 1018, 1019, 7, 68, 2, 2, 1019, 1020, 5, 10, 6, 2, 1020, 1022, 7, 94, 2, 2, 1021, 1023, 5, 272, 137, 2, 1022, 1021, 3, 2, 2, 2, 1022, 1023, 3, 2, 2, 2, 1023, 1024, 3, 2, 2, 2, 1024, 1025, 7, 95, 2, 2, 1025, 1027, 3, 2, 2, 2, 1026, 1015, 3, 2, 2, 2, 1026, 1017, 3, 2, 2, 2, 1026, 1018, 3, 2, 2, 2, 1027, 211, 3, 2, 2, 2, 1028, 1029, 7, 69, 2, 2, 1029, 213, 3, 2, 2, 2, 1030, 1031, 7, 105, 2, 2, 1031, 215, 3, 2, 2, 2, 1032, 1035, 5, 218, 110, 2, 1033, 1035, 5, 220, 111, 2, 1034, 1032, 3, 2, 2, 2, 1034, 1033, 3, 2, 2, 2, 1035, 217, 3, 2, 2, 2, 1036, 1037, 7, 70, 2, 2, 1037, 1039, 7, 94, 2, 2, 1038, 1040, 5, 224, 113, 2, 1039, 1038, 3, 2, 2, 2, 1039, 1040, 3, 2, 2, 2, 1040, 1044, 3, 2, 2, 2, 1041, 1043, 5, 222, 112, 2, 1042, 1041, 3, 2, 2, 2, 1043, 1046, 3, 2, 2, 2, 1044, 1042, 3, 2, 2, 2, 1044, 1045, 3, 2, 2, 2, 1045, 1047, 3, 2, 2, 2, 1046, 1044, 3, 2, 2, 2, 1047, 1048, 7, 95, 2, 2, 1048, 1049, 7, 91, 2, 2, 1049, 219, 3, 2, 2, 2, 1050, 1051, 7, 71, 2, 2, 1051, 1053, 7, 94, 2, 2, 1052, 1054, 5, 224, 113, 2, 1053, 1052, 3, 2, 2, 2, 1053, 1054, 3, 2, 2, 2, 1054, 1058, 3, 2, 2, 2, 1055, 1057, 5, 222, 112, 2, 1056, 1055, 3, 2, 2, 2, 1057, 1060, 3, 2, 2, 2, 1058, 1056, 3, 2, 2, 2, 1058, 1059, 3, 2, 2, 2, 1059, 1061, 3, 2, 2, 2, 1060, 1058, 3, 2, 2, 2, 1061, 1062, 7, 95, 2, 2, 1062, 1063, 7, 91, 2, 2, 1063, 221, 3, 2, 2, 2, 1064, 1065, 7, 10, 2, 2, 1065, 1066, 7, 66, 2, 2, 1066, 1067, 7, 11, 2, 2, 1067, 1087, 5, 226, 114, 2, 1068, 1069, 7, 10, 2, 2, 1069, 1070, 7, 8, 2, 2, 1070, 1071, 7, 11, 2, 2, 1071, 1087, 5, 26, 14, 2, 1072, 1073, 7, 10, 2, 2, 1073, 1074, 7, 72, 2, 2, 1074, 1075, 7, 11, 2, 2, 1075, 1087, 5, 262, 132, 2, 1076, 1077, 7, 10, 2, 2, 1077, 1078, 7, 47, 2, 2, 1078, 1079, 7, 11, 2, 2, 1079, 1087, 5, 258, 130, 2, 1080, 1081, 7, 10, 2, 2, 1081, 1082, 7, 40, 2, 2, 1082, 1083, 7, 11, 2, 2, 1083, 1087, 5, 124, 63, 2, 1084, 1085, 7, 10, 2, 2, 1085, 1087, 5, 276, 139, 2, 1086, 1064, 3, 2, 2, 2, 1086, 1068, 3, 2, 2, 2, 1086, 1072, 3, 2, 2, 2, 1086, 1076, 3, 2, 2, 2, 1086, 1080, 3, 2, 2, 2, 1086, 1084, 3, 2, 2, 2, 1087, 223, 3, 2, 2, 2, 1088, 1089, 7, 105, 2, 2, 1089, 225, 3, 2, 2, 2, 1090, 1093, 5, 230, 116, 2, 1091, 1093, 5, 228, 115, 2, 1092, 1090, 3, 2, 2, 2, 1092, 1091, 3, 2, 2, 2, 1093, 227, 3, 2, 2, 2, 1094, 1095, 5, 230, 116, 2, 1095, 1096, 7, 73, 2, 2, 1096, 1097, 5, 226, 114, 2, 1097, 1098, 7, 11, 2, 2, 1098, 1099, 5, 226, 114, 2, 1099, 229, 3, 2, 2, 2, 1100, 1105, 5, 232, 117, 2, 1101, 1102, 7, 74, 2, 2, 1102, 1104, 5, 232, 117, 2, 1103, 1101, 3, 2, 2, 2, 1104, 1107, 3, 2, 2, 2, 1105, 1103, 3, 2, 2, 2, 1105, 1106, 3, 2, 2, 2, 1106, 231, 3, 2, 2, 2, 1107, 1105, 3, 2, 2, 2, 1108, 1113, 5, 234, 118, 2, 1109, 1110, 7, 75, 2, 2, 1110, 1112, 5, 234, 118, 2, 1111, 1109, 3, 2, 2, 2, 1112, 1115, 3, 2, 2, 2, 1113, 1111, 3, 2, 2, 2, 1113, 1114, 3, 2, 2, 2, 1114, 233, 3, 2, 2, 2, 1115, 1113, 3, 2, 2, 2, 1116, 1121, 5, 236, 119, 2, 1117, 1118, 7, 76, 2, 2, 1118, 1120, 5, 236, 119, 2, 1119, 1117, 3, 2, 2, 2, 1120, 1123, 3, 2, 2, 2, 1121, 1119, 3, 2, 2, 2, 1121, 1122, 3, 2, 2, 2, 1122, 235, 3, 2, 2, 2, 1123, 1121, 3, 2, 2, 2, 1124, 1125, 7, 77, 2, 2, 1125, 1128, 5, 236, 119, 2, 1126, 1128, 5, 238, 120, 2, 1127, 1124, 3, 2, 2, 2, 1127, 1126, 3, 2, 2, 2, 1128, 237, 3, 2, 2, 2, 1129, 1130, 8, 120, 1, 2, 1130, 1131, 5, 242, 122, 2, 1131, 1138, 3, 2, 2, 2, 1132, 1133, 12, 3, 2, 2, 1133, 1134, 5, 240, 121, 2, 1134, 1135, 5, 242, 122, 2, 1135, 1137, 3, 2, 2, 2, 1136, 1132, 3, 2, 2, 2, 1137, 1140, 3, 2, 2, 2, 1138, 1136, 3, 2, 2, 2, 1138, 1139, 3, 2, 2, 2, 1139, 239, 3, 2, 2, 2, 1140, 1138, 3, 2, 2, 2, 1141, 1142, 9, 7, 2, 2, 1142, 241, 3, 2, 2, 2, 1143, 1144, 8, 122, 1, 2, 1144, 1145, 5, 246, 124, 2, 1145, 1152, 3, 2, 2, 2, 1146, 1147, 12, 3, 2, 2, 1147, 1148, 5, 244, 123, 2, 1148, 1149, 5, 246, 124, 2, 1149, 1151, 3, 2, 2, 2, 1150, 1146, 3, 2, 2, 2, 1151, 1154, 3, 2, 2, 2, 1152, 1150, 3, 2, 2, 2, 1152, 1153, 3, 2, 2, 2, 1153, 243, 3, 2, 2, 2, 1154, 1152, 3, 2, 2, 2, 1155, 1156, 9, 8, 2, 2, 1156, 245, 3, 2, 2, 2, 1157, 1158, 8, 124, 1, 2, 1158, 1159, 5, 250, 126, 2, 1159, 1166, 3, 2, 2, 2, 1160, 1161, 12, 3, 2, 2, 1161, 1162, 5, 248, 125, 2, 1162, 1163, 5, 250, 126, 2, 1163, 1165, 3, 2, 2, 2, 1164, 1160, 3, 2, 2, 2, 1165, 1168, 3, 2, 2, 2, 1166, 1164, 3, 2, 2, 2, 1166, 1167, 3, 2, 2, 2, 1167, 247, 3, 2, 2, 2, 1168, 1166, 3, 2, 2, 2, 1169, 1170, 9, 9, 2, 2, 1170, 249, 3, 2, 2, 2, 1171, 1175, 5, 252, 127, 2, 1172, 1173, 7, 85, 2, 2, 1173, 1175, 5, 250, 126, 2, 1174, 1171, 3, 2, 2, 2, 1174, 1172, 3, 2, 2, 2, 1175, 251, 3, 2, 2, 2, 1176, 1177, 8, 127, 1, 2, 1177, 1178, 5, 256, 129, 2, 1178, 1209, 3, 2, 2, 2, 1179, 1180, 12, 7, 2, 2, 1180, 1181, 7, 4, 2, 2, 1181, 1182, 7, 43, 2, 2, 1182, 1183, 7, 94, 2, 2, 1183, 1184, 5, 102, 52, 2, 1184, 1185, 7, 95, 2, 2, 1185, 1208, 3, 2, 2, 2, 1186, 1187, 12, 6, 2, 2, 1187, 1188, 7, 4, 2, 2, 1188, 1189, 7, 6, 2, 2, 1189, 1190, 7, 94, 2, 2, 1190, 1191, 5, 102, 52, 2, 1191, 1192, 7, 95, 2, 2, 1192, 1208, 3, 2, 2, 2, 1193, 1194, 12, 5, 2, 2, 1194, 1195, 7, 92, 2, 2, 1195, 1196, 5, 226, 114, 2, 1196, 1197, 7, 93, 2, 2, 1197, 1208, 3, 2, 2, 2, 1198, 1199, 12, 4, 2, 2, 1199, 1201, 7, 94, 2, 2, 1200, 1202, 5, 272, 137, 2, 1201, 1200, 3, 2, 2, 2, 1201, 1202, 3, 2, 2, 2, 1202, 1203, 3, 2, 2, 2, 1203, 1208, 7, 95, 2, 2, 1204, 1205, 12, 3, 2, 2, 1205, 1206, 7, 4, 2, 2, 1206, 1208, 5, 60, 31, 2, 1207, 1179, 3, 2, 2, 2, 1207, 1186, 3, 2, 2, 2, 1207, 1193, 3, 2, 2, 2, 1207, 1198, 3, 2, 2, 2, 1207, 1204, 3, 2, 2, 2, 1208, 1211, 3, 2, 2, 2, 1209, 1207, 3, 2, 2, 2, 1209, 1210, 3, 2, 2, 2, 1210, 253, 3, 2, 2, 2, 1211, 1209, 3, 2, 2, 2, 1212, 1213, 5, 252, 127, 2, 1213, 1214, 7, 4, 2, 2, 1214, 1215, 5, 60, 31, 2, 1215, 255, 3, 2, 2, 2, 1216, 1224, 5, 258, 130, 2, 1217, 1224, 7, 89, 2, 2, 1218, 1224, 7, 105, 2, 2, 1219, 1220, 7, 94, 2, 2, 1220, 1221, 5, 226, 114, 2, 1221, 1222, 7, 95, 2, 2, 1222, 1224, 3, 2, 2, 2, 1223, 1216, 3, 2, 2, 2, 1223, 1217, 3, 2, 2, 2, 1223, 1218, 3, 2, 2, 2, 1223, 1219, 3, 2, 2, 2, 1224, 257, 3, 2, 2, 2, 1225, 1235, 5, 278, 140, 2, 1226, 1235, 7, 100, 2, 2, 1227, 1235, 5, 280, 141, 2, 1228, 1235, 7, 104, 2, 2, 1229, 1235, 7, 99, 2, 2, 1230, 1235, 5, 264, 133, 2, 1231, 1235, 5, 50, 26, 2, 1232, 1235, 5, 260, 131, 2, 1233, 1235, 5, 262, 132, 2, 1234, 1225, 3, 2, 2, 2, 1234, 1226, 3, 2, 2, 2, 1234, 1227, 3, 2, 2, 2, 1234, 1228, 3, 2, 2, 2, 1234, 1229, 3, 2, 2, 2, 1234, 1230, 3, 2, 2, 2, 1234, 1231, 3, 2, 2, 2, 1234, 1232, 3, 2, 2, 2, 1234, 1233, 3, 2, 2, 2, 1235, 259, 3, 2, 2, 2, 1236, 1237, 7, 92, 2, 2, 1237, 1242, 5, 226, 114, 2, 1238, 1239, 7, 10, 2, 2, 1239, 1241, 5, 226, 114, 2, 1240, 1238, 3, 2, 2, 2, 1241, 1244, 3, 2, 2, 2, 1242, 1240, 3, 2, 2, 2, 1242, 1243, 3, 2, 2, 2, 1243, 1245, 3, 2, 2, 2, 1244, 1242, 3, 2, 2, 2, 1245, 1246, 7, 93, 2, 2, 1246, 261, 3, 2, 2, 2, 1247, 1248, 7, 72, 2, 2, 1248, 1249, 7, 94, 2, 2, 1249, 1250, 5, 226, 114, 2, 1250, 1251, 7, 10, 2, 2, 1251, 1252, 5, 226, 114, 2, 1252, 1253, 7, 95, 2, 2, 1253, 1261, 3, 2, 2, 2, 1254, 1255, 7, 92, 2, 2, 1255, 1256, 5, 226, 114, 2, 1256, 1257, 7, 90, 2, 2, 1257, 1258, 5, 226, 114, 2, 1258, 1259, 7, 93, 2, 2, 1259, 1261, 3, 2, 2, 2, 1260, 1247, 3, 2, 2, 2, 1260, 1254, 3, 2, 2, 2, 1261, 263, 3, 2, 2, 2, 1262, 1263, 5, 60, 31, 2, 1263, 1264, 7, 4, 2, 2, 1264, 1266, 3, 2, 2, 2, 1265, 1262, 3, 2, 2, 2, 1266, 1269, 3, 2, 2, 2, 1267, 1265, 3, 2, 2, 2, 1267, 1268, 3, 2, 2, 2, 1268, 1270, 3, 2, 2, 2, 1269, 1267, 3, 2, 2, 2, 1270, 1271, 5, 60, 31, 2, 1271, 265, 3, 2, 2, 2, 1272, 1277, 5, 268, 135, 2, 1273, 1274, 7, 10, 2, 2, 1274, 1276, 5, 268, 135, 2, 1275, 1273, 3, 2, 2, 2, 1276, 1279, 3, 2, 2, 2, 1277, 1275, 3, 2, 2, 2, 1277, 1278, 3, 2, 2, 2, 1278, 267, 3, 2, 2, 2, 1279, 1277, 3, 2, 2, 2, 1280, 1281, 5, 270, 136, 2, 1281, 1282, 7, 11, 2, 2, 1282, 1285, 5, 102, 52, 2, 1283, 1284, 7, 23, 2, 2, 1284, 1286, 5, 150, 76, 2, 1285, 1283, 3, 2, 2, 2, 1285, 1286, 3, 2, 2, 2, 1286, 269, 3, 2, 2, 2, 1287, 1288, 7, 105, 2, 2, 1288, 271, 3, 2, 2, 2, 1289, 1294, 5, 274, 138, 2, 1290, 1291, 7, 10, 2, 2, 1291, 1293, 5, 274, 138, 2, 1292, 1290, 3, 2, 2, 2, 1293, 1296, 3, 2, 2, 2, 1294, 1292, 3, 2, 2, 2, 1294, 1295, 3, 2, 2, 2, 1295, 1301, 3, 2, 2, 2, 1296, 1294, 3, 2, 2, 2, 1297, 1298, 7, 10, 2, 2, 1298, 1300, 5, 276, 139, 2, 1299, 1297, 3, 2, 2, 2, 1300, 1303, 3, 2, 2, 2, 1301, 1299, 3, 2, 2, 2, 1301, 1302, 3, 2, 2, 2, 1302, 1313, 3, 2, 2, 2, 1303, 1301, 3, 2, 2, 2, 1304, 1309, 5, 276, 139, 2, 1305, 1306, 7, 10, 2, 2, 1306, 1308, 5, 276, 139, 2, 1307, 1305, 3, 2, 2, 2, 1308, 1311, 3, 2, 2, 2, 1309, 1307, 3, 2, 2, 2, 1309, 1310, 3, 2, 2, 2, 1310, 1313, 3, 2, 2, 2, 1311, 1309, 3, 2, 2, 2, 1312, 1289, 3, 2, 2, 2, 1312, 1304, 3, 2, 2, 2, 1313, 273, 3, 2, 2, 2, 1314, 1315, 5, 226, 114, 2, 1315, 275, 3, 2, 2, 2, 1316, 1317, 5, 270, 136, 2, 1317, 1318, 7, 11, 2, 2, 1318, 1319, 5, 226, 114, 2, 1319, 277, 3, 2, 2, 2, 1320, 1323, 7, 100, 2, 2, 1321, 1323, 5, 280, 141, 2, 1322, 1320, 3, 2, 2, 2, 1322, 1321, 3, 2, 2, 2, 1323, 1324, 3, 2, 2, 2, 1324, 1325, 5, 26, 14, 2, 1325, 279, 3, 2, 2, 2, 1326, 1327, 9, 10, 2, 2, 1327, 281, 3, 2, 2, 2, 128, 285, 291, 303, 307, 317, 331, 360, 367, 379, 383, 391, 397, 410, 419, 436, 443, 451, 456, 461, 468, 477, 485, 490, 495, 502, 509, 516, 518, 523, 528, 536, 541, 550, 557, 559, 564, 569, 575, 580, 588, 593, 599, 610, 624, 630, 636, 644, 651, 655, 659, 664, 682, 686, 692, 696, 699, 714, 723, 748, 758, 765, 772, 776, 784, 792, 794, 805, 818, 824, 828, 833, 851, 855, 859, 864, 876, 886, 892, 900, 907, 912, 915, 923, 927, 934, 939, 944, 953, 960, 976, 991, 999, 1004, 1013, 1022, 1026, 1034, 1039, 1044, 1053, 1058, 1086, 1092, 1105, 1113, 1121, 1127, 1138, 1152, 1166, 1174, 1201, 1207, 1209, 1223, 1234, 1242, 1260, 1267, 1277, 1285, 1294, 1301, 1309, 1312, 1322] \ No newline at end of file diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.tokens b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.tokens new file mode 100644 index 00000000..df6dffc4 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2.tokens @@ -0,0 +1,197 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +T__13=14 +T__14=15 +T__15=16 +T__16=17 +T__17=18 +T__18=19 +T__19=20 +T__20=21 +T__21=22 +T__22=23 +T__23=24 +T__24=25 +T__25=26 +T__26=27 +T__27=28 +T__28=29 +T__29=30 +T__30=31 +T__31=32 +T__32=33 +T__33=34 +T__34=35 +T__35=36 +T__36=37 +T__37=38 +T__38=39 +T__39=40 +T__40=41 +T__41=42 +T__42=43 +T__43=44 +T__44=45 +T__45=46 +T__46=47 +T__47=48 +T__48=49 +T__49=50 +T__50=51 +T__51=52 +T__52=53 +T__53=54 +T__54=55 +T__55=56 +T__56=57 +T__57=58 +T__58=59 +T__59=60 +T__60=61 +T__61=62 +T__62=63 +T__63=64 +T__64=65 +T__65=66 +T__66=67 +T__67=68 +T__68=69 +T__69=70 +T__70=71 +T__71=72 +T__72=73 +T__73=74 +T__74=75 +T__75=76 +T__76=77 +T__77=78 +T__78=79 +T__79=80 +T__80=81 +T__81=82 +T__82=83 +T__83=84 +T__84=85 +T__85=86 +T__86=87 +T__87=88 +NEWLINE=89 +OPEN_BRACK=90 +CLOSE_BRACK=91 +OPEN_PAREN=92 +CLOSE_PAREN=93 +SKIP_=94 +BLOCK_COMMENT=95 +LINE_COMMENT=96 +StringLiteral=97 +FloatLiteral=98 +UintLiteral=99 +HexUintLiteral=100 +IntLiteral=101 +BoolLiteral=102 +Identifier=103 +INDENT=104 +DEDENT=105 +'import'=1 +'.'=2 +'type'=3 +'is'=4 +'SI'=5 +'unit'=6 +'of'=7 +','=8 +':'=9 +'factor'=10 +'offset'=11 +'kg'=12 +'m'=13 +'s'=14 +'A'=15 +'K'=16 +'mol'=17 +'cd'=18 +'rad'=19 +'enum'=20 +'='=21 +'!'=22 +'=='=23 +'struct'=24 +'inherits'=25 +'actor'=26 +'scenario'=27 +'action'=28 +'modifier'=29 +'extend'=30 +'global'=31 +'list'=32 +'int'=33 +'uint'=34 +'float'=35 +'bool'=36 +'string'=37 +'event'=38 +'if'=39 +'@'=40 +'as'=41 +'rise'=42 +'fall'=43 +'elapsed'=44 +'every'=45 +'var'=46 +'sample'=47 +'with'=48 +'keep'=49 +'default'=50 +'hard'=51 +'remove_default'=52 +'on'=53 +'do'=54 +'serial'=55 +'one_of'=56 +'parallel'=57 +'wait'=58 +'emit'=59 +'call'=60 +'until'=61 +'def'=62 +'->'=63 +'expression'=64 +'undefined'=65 +'external'=66 +'only'=67 +'cover'=68 +'record'=69 +'range'=70 +'?'=71 +'=>'=72 +'or'=73 +'and'=74 +'not'=75 +'!='=76 +'<'=77 +'<='=78 +'>'=79 +'>='=80 +'in'=81 +'+'=82 +'-'=83 +'*'=84 +'/'=85 +'%'=86 +'it'=87 +'..'=88 +'['=90 +']'=91 +'('=92 +')'=93 diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.interp b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.interp new file mode 100644 index 00000000..d91df2d0 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.interp @@ -0,0 +1,339 @@ +token literal names: +null +'import' +'.' +'type' +'is' +'SI' +'unit' +'of' +',' +':' +'factor' +'offset' +'kg' +'m' +'s' +'A' +'K' +'mol' +'cd' +'rad' +'enum' +'=' +'!' +'==' +'struct' +'inherits' +'actor' +'scenario' +'action' +'modifier' +'extend' +'global' +'list' +'int' +'uint' +'float' +'bool' +'string' +'event' +'if' +'@' +'as' +'rise' +'fall' +'elapsed' +'every' +'var' +'sample' +'with' +'keep' +'default' +'hard' +'remove_default' +'on' +'do' +'serial' +'one_of' +'parallel' +'wait' +'emit' +'call' +'until' +'def' +'->' +'expression' +'undefined' +'external' +'only' +'cover' +'record' +'range' +'?' +'=>' +'or' +'and' +'not' +'!=' +'<' +'<=' +'>' +'>=' +'in' +'+' +'-' +'*' +'/' +'%' +'it' +'..' +null +'[' +']' +'(' +')' +null +null +null +null +null +null +null +null +null +null + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +NEWLINE +OPEN_BRACK +CLOSE_BRACK +OPEN_PAREN +CLOSE_PAREN +SKIP_ +BLOCK_COMMENT +LINE_COMMENT +StringLiteral +FloatLiteral +UintLiteral +HexUintLiteral +IntLiteral +BoolLiteral +Identifier + +rule names: +T__0 +T__1 +T__2 +T__3 +T__4 +T__5 +T__6 +T__7 +T__8 +T__9 +T__10 +T__11 +T__12 +T__13 +T__14 +T__15 +T__16 +T__17 +T__18 +T__19 +T__20 +T__21 +T__22 +T__23 +T__24 +T__25 +T__26 +T__27 +T__28 +T__29 +T__30 +T__31 +T__32 +T__33 +T__34 +T__35 +T__36 +T__37 +T__38 +T__39 +T__40 +T__41 +T__42 +T__43 +T__44 +T__45 +T__46 +T__47 +T__48 +T__49 +T__50 +T__51 +T__52 +T__53 +T__54 +T__55 +T__56 +T__57 +T__58 +T__59 +T__60 +T__61 +T__62 +T__63 +T__64 +T__65 +T__66 +T__67 +T__68 +T__69 +T__70 +T__71 +T__72 +T__73 +T__74 +T__75 +T__76 +T__77 +T__78 +T__79 +T__80 +T__81 +T__82 +T__83 +T__84 +T__85 +T__86 +T__87 +NEWLINE +OPEN_BRACK +CLOSE_BRACK +OPEN_PAREN +CLOSE_PAREN +SKIP_ +SPACES +LINE_JOINING +RN +BLOCK_COMMENT +LINE_COMMENT +StringLiteral +Shortstring +ShortstringElem +ShortstringChar +Longstring +LongstringElem +LongstringChar +StringEscapeSeq +FloatLiteral +UintLiteral +HexUintLiteral +IntLiteral +BoolLiteral +Identifier +NonVerticalLineChar +Digit +HexDigit + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 105, 876, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, 4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101, 4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106, 9, 106, 4, 107, 9, 107, 4, 108, 9, 108, 4, 109, 9, 109, 4, 110, 9, 110, 4, 111, 9, 111, 4, 112, 9, 112, 4, 113, 9, 113, 4, 114, 9, 114, 4, 115, 9, 115, 4, 116, 9, 116, 4, 117, 9, 117, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 25, 3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, 3, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 47, 3, 47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3, 48, 3, 48, 3, 48, 3, 49, 3, 49, 3, 49, 3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 54, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 59, 3, 59, 3, 59, 3, 59, 3, 59, 3, 60, 3, 60, 3, 60, 3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 62, 3, 62, 3, 62, 3, 63, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 3, 72, 3, 72, 3, 73, 3, 73, 3, 73, 3, 74, 3, 74, 3, 74, 3, 75, 3, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 76, 3, 76, 3, 77, 3, 77, 3, 77, 3, 78, 3, 78, 3, 79, 3, 79, 3, 79, 3, 80, 3, 80, 3, 81, 3, 81, 3, 81, 3, 82, 3, 82, 3, 82, 3, 83, 3, 83, 3, 84, 3, 84, 3, 85, 3, 85, 3, 86, 3, 86, 3, 87, 3, 87, 3, 88, 3, 88, 3, 88, 3, 89, 3, 89, 3, 89, 3, 90, 3, 90, 3, 90, 5, 90, 658, 10, 90, 3, 90, 3, 90, 5, 90, 662, 10, 90, 3, 90, 5, 90, 665, 10, 90, 5, 90, 667, 10, 90, 3, 90, 3, 90, 3, 91, 3, 91, 3, 91, 3, 92, 3, 92, 3, 92, 3, 93, 3, 93, 3, 93, 3, 94, 3, 94, 3, 94, 3, 95, 3, 95, 5, 95, 685, 10, 95, 3, 95, 3, 95, 3, 96, 6, 96, 690, 10, 96, 13, 96, 14, 96, 691, 3, 97, 3, 97, 5, 97, 696, 10, 97, 3, 97, 5, 97, 699, 10, 97, 3, 97, 3, 97, 3, 98, 5, 98, 704, 10, 98, 3, 98, 3, 98, 3, 99, 3, 99, 3, 99, 3, 99, 7, 99, 712, 10, 99, 12, 99, 14, 99, 715, 11, 99, 3, 99, 3, 99, 3, 99, 3, 99, 3, 99, 3, 100, 3, 100, 7, 100, 724, 10, 100, 12, 100, 14, 100, 727, 11, 100, 3, 100, 3, 100, 3, 101, 3, 101, 5, 101, 733, 10, 101, 3, 102, 3, 102, 7, 102, 737, 10, 102, 12, 102, 14, 102, 740, 11, 102, 3, 102, 3, 102, 3, 102, 7, 102, 745, 10, 102, 12, 102, 14, 102, 748, 11, 102, 3, 102, 5, 102, 751, 10, 102, 3, 103, 3, 103, 5, 103, 755, 10, 103, 3, 104, 3, 104, 3, 105, 3, 105, 3, 105, 3, 105, 3, 105, 7, 105, 764, 10, 105, 12, 105, 14, 105, 767, 11, 105, 3, 105, 3, 105, 3, 105, 3, 105, 3, 105, 3, 105, 3, 105, 3, 105, 7, 105, 777, 10, 105, 12, 105, 14, 105, 780, 11, 105, 3, 105, 3, 105, 3, 105, 5, 105, 785, 10, 105, 3, 106, 3, 106, 5, 106, 789, 10, 106, 3, 107, 3, 107, 3, 108, 3, 108, 3, 108, 3, 108, 5, 108, 797, 10, 108, 3, 109, 5, 109, 800, 10, 109, 3, 109, 7, 109, 803, 10, 109, 12, 109, 14, 109, 806, 11, 109, 3, 109, 3, 109, 6, 109, 810, 10, 109, 13, 109, 14, 109, 811, 3, 109, 3, 109, 5, 109, 816, 10, 109, 3, 109, 6, 109, 819, 10, 109, 13, 109, 14, 109, 820, 5, 109, 823, 10, 109, 3, 110, 6, 110, 826, 10, 110, 13, 110, 14, 110, 827, 3, 111, 3, 111, 3, 111, 3, 111, 6, 111, 834, 10, 111, 13, 111, 14, 111, 835, 3, 112, 3, 112, 6, 112, 840, 10, 112, 13, 112, 14, 112, 841, 3, 113, 3, 113, 3, 113, 3, 113, 3, 113, 3, 113, 3, 113, 3, 113, 3, 113, 5, 113, 853, 10, 113, 3, 114, 3, 114, 7, 114, 857, 10, 114, 12, 114, 14, 114, 860, 11, 114, 3, 114, 3, 114, 6, 114, 864, 10, 114, 13, 114, 14, 114, 865, 3, 114, 5, 114, 869, 10, 114, 3, 115, 3, 115, 3, 116, 3, 116, 3, 117, 3, 117, 3, 713, 2, 118, 3, 3, 5, 4, 7, 5, 9, 6, 11, 7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16, 31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 43, 23, 45, 24, 47, 25, 49, 26, 51, 27, 53, 28, 55, 29, 57, 30, 59, 31, 61, 32, 63, 33, 65, 34, 67, 35, 69, 36, 71, 37, 73, 38, 75, 39, 77, 40, 79, 41, 81, 42, 83, 43, 85, 44, 87, 45, 89, 46, 91, 47, 93, 48, 95, 49, 97, 50, 99, 51, 101, 52, 103, 53, 105, 54, 107, 55, 109, 56, 111, 57, 113, 58, 115, 59, 117, 60, 119, 61, 121, 62, 123, 63, 125, 64, 127, 65, 129, 66, 131, 67, 133, 68, 135, 69, 137, 70, 139, 71, 141, 72, 143, 73, 145, 74, 147, 75, 149, 76, 151, 77, 153, 78, 155, 79, 157, 80, 159, 81, 161, 82, 163, 83, 165, 84, 167, 85, 169, 86, 171, 87, 173, 88, 175, 89, 177, 90, 179, 91, 181, 92, 183, 93, 185, 94, 187, 95, 189, 96, 191, 2, 193, 2, 195, 2, 197, 97, 199, 98, 201, 99, 203, 2, 205, 2, 207, 2, 209, 2, 211, 2, 213, 2, 215, 2, 217, 100, 219, 101, 221, 102, 223, 103, 225, 104, 227, 105, 229, 2, 231, 2, 233, 2, 3, 2, 13, 4, 2, 11, 11, 34, 34, 4, 2, 12, 12, 14, 15, 7, 2, 12, 12, 15, 15, 36, 36, 41, 41, 94, 94, 3, 2, 94, 94, 4, 2, 45, 45, 47, 47, 4, 2, 71, 71, 103, 103, 4, 2, 67, 92, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 3, 2, 126, 126, 3, 2, 50, 59, 5, 2, 50, 59, 67, 72, 99, 104, 2, 896, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, 2, 2, 2, 2, 51, 3, 2, 2, 2, 2, 53, 3, 2, 2, 2, 2, 55, 3, 2, 2, 2, 2, 57, 3, 2, 2, 2, 2, 59, 3, 2, 2, 2, 2, 61, 3, 2, 2, 2, 2, 63, 3, 2, 2, 2, 2, 65, 3, 2, 2, 2, 2, 67, 3, 2, 2, 2, 2, 69, 3, 2, 2, 2, 2, 71, 3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2, 2, 87, 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91, 3, 2, 2, 2, 2, 93, 3, 2, 2, 2, 2, 95, 3, 2, 2, 2, 2, 97, 3, 2, 2, 2, 2, 99, 3, 2, 2, 2, 2, 101, 3, 2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2, 2, 2, 107, 3, 2, 2, 2, 2, 109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113, 3, 2, 2, 2, 2, 115, 3, 2, 2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2, 2, 121, 3, 2, 2, 2, 2, 123, 3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3, 2, 2, 2, 2, 129, 3, 2, 2, 2, 2, 131, 3, 2, 2, 2, 2, 133, 3, 2, 2, 2, 2, 135, 3, 2, 2, 2, 2, 137, 3, 2, 2, 2, 2, 139, 3, 2, 2, 2, 2, 141, 3, 2, 2, 2, 2, 143, 3, 2, 2, 2, 2, 145, 3, 2, 2, 2, 2, 147, 3, 2, 2, 2, 2, 149, 3, 2, 2, 2, 2, 151, 3, 2, 2, 2, 2, 153, 3, 2, 2, 2, 2, 155, 3, 2, 2, 2, 2, 157, 3, 2, 2, 2, 2, 159, 3, 2, 2, 2, 2, 161, 3, 2, 2, 2, 2, 163, 3, 2, 2, 2, 2, 165, 3, 2, 2, 2, 2, 167, 3, 2, 2, 2, 2, 169, 3, 2, 2, 2, 2, 171, 3, 2, 2, 2, 2, 173, 3, 2, 2, 2, 2, 175, 3, 2, 2, 2, 2, 177, 3, 2, 2, 2, 2, 179, 3, 2, 2, 2, 2, 181, 3, 2, 2, 2, 2, 183, 3, 2, 2, 2, 2, 185, 3, 2, 2, 2, 2, 187, 3, 2, 2, 2, 2, 189, 3, 2, 2, 2, 2, 197, 3, 2, 2, 2, 2, 199, 3, 2, 2, 2, 2, 201, 3, 2, 2, 2, 2, 217, 3, 2, 2, 2, 2, 219, 3, 2, 2, 2, 2, 221, 3, 2, 2, 2, 2, 223, 3, 2, 2, 2, 2, 225, 3, 2, 2, 2, 2, 227, 3, 2, 2, 2, 3, 235, 3, 2, 2, 2, 5, 242, 3, 2, 2, 2, 7, 244, 3, 2, 2, 2, 9, 249, 3, 2, 2, 2, 11, 252, 3, 2, 2, 2, 13, 255, 3, 2, 2, 2, 15, 260, 3, 2, 2, 2, 17, 263, 3, 2, 2, 2, 19, 265, 3, 2, 2, 2, 21, 267, 3, 2, 2, 2, 23, 274, 3, 2, 2, 2, 25, 281, 3, 2, 2, 2, 27, 284, 3, 2, 2, 2, 29, 286, 3, 2, 2, 2, 31, 288, 3, 2, 2, 2, 33, 290, 3, 2, 2, 2, 35, 292, 3, 2, 2, 2, 37, 296, 3, 2, 2, 2, 39, 299, 3, 2, 2, 2, 41, 303, 3, 2, 2, 2, 43, 308, 3, 2, 2, 2, 45, 310, 3, 2, 2, 2, 47, 312, 3, 2, 2, 2, 49, 315, 3, 2, 2, 2, 51, 322, 3, 2, 2, 2, 53, 331, 3, 2, 2, 2, 55, 337, 3, 2, 2, 2, 57, 346, 3, 2, 2, 2, 59, 353, 3, 2, 2, 2, 61, 362, 3, 2, 2, 2, 63, 369, 3, 2, 2, 2, 65, 376, 3, 2, 2, 2, 67, 381, 3, 2, 2, 2, 69, 385, 3, 2, 2, 2, 71, 390, 3, 2, 2, 2, 73, 396, 3, 2, 2, 2, 75, 401, 3, 2, 2, 2, 77, 408, 3, 2, 2, 2, 79, 414, 3, 2, 2, 2, 81, 417, 3, 2, 2, 2, 83, 419, 3, 2, 2, 2, 85, 422, 3, 2, 2, 2, 87, 427, 3, 2, 2, 2, 89, 432, 3, 2, 2, 2, 91, 440, 3, 2, 2, 2, 93, 446, 3, 2, 2, 2, 95, 450, 3, 2, 2, 2, 97, 457, 3, 2, 2, 2, 99, 462, 3, 2, 2, 2, 101, 467, 3, 2, 2, 2, 103, 475, 3, 2, 2, 2, 105, 480, 3, 2, 2, 2, 107, 495, 3, 2, 2, 2, 109, 498, 3, 2, 2, 2, 111, 501, 3, 2, 2, 2, 113, 508, 3, 2, 2, 2, 115, 515, 3, 2, 2, 2, 117, 524, 3, 2, 2, 2, 119, 529, 3, 2, 2, 2, 121, 534, 3, 2, 2, 2, 123, 539, 3, 2, 2, 2, 125, 545, 3, 2, 2, 2, 127, 549, 3, 2, 2, 2, 129, 552, 3, 2, 2, 2, 131, 563, 3, 2, 2, 2, 133, 573, 3, 2, 2, 2, 135, 582, 3, 2, 2, 2, 137, 587, 3, 2, 2, 2, 139, 593, 3, 2, 2, 2, 141, 600, 3, 2, 2, 2, 143, 606, 3, 2, 2, 2, 145, 608, 3, 2, 2, 2, 147, 611, 3, 2, 2, 2, 149, 614, 3, 2, 2, 2, 151, 618, 3, 2, 2, 2, 153, 622, 3, 2, 2, 2, 155, 625, 3, 2, 2, 2, 157, 627, 3, 2, 2, 2, 159, 630, 3, 2, 2, 2, 161, 632, 3, 2, 2, 2, 163, 635, 3, 2, 2, 2, 165, 638, 3, 2, 2, 2, 167, 640, 3, 2, 2, 2, 169, 642, 3, 2, 2, 2, 171, 644, 3, 2, 2, 2, 173, 646, 3, 2, 2, 2, 175, 648, 3, 2, 2, 2, 177, 651, 3, 2, 2, 2, 179, 666, 3, 2, 2, 2, 181, 670, 3, 2, 2, 2, 183, 673, 3, 2, 2, 2, 185, 676, 3, 2, 2, 2, 187, 679, 3, 2, 2, 2, 189, 684, 3, 2, 2, 2, 191, 689, 3, 2, 2, 2, 193, 693, 3, 2, 2, 2, 195, 703, 3, 2, 2, 2, 197, 707, 3, 2, 2, 2, 199, 721, 3, 2, 2, 2, 201, 732, 3, 2, 2, 2, 203, 750, 3, 2, 2, 2, 205, 754, 3, 2, 2, 2, 207, 756, 3, 2, 2, 2, 209, 784, 3, 2, 2, 2, 211, 788, 3, 2, 2, 2, 213, 790, 3, 2, 2, 2, 215, 796, 3, 2, 2, 2, 217, 799, 3, 2, 2, 2, 219, 825, 3, 2, 2, 2, 221, 829, 3, 2, 2, 2, 223, 837, 3, 2, 2, 2, 225, 852, 3, 2, 2, 2, 227, 868, 3, 2, 2, 2, 229, 870, 3, 2, 2, 2, 231, 872, 3, 2, 2, 2, 233, 874, 3, 2, 2, 2, 235, 236, 7, 107, 2, 2, 236, 237, 7, 111, 2, 2, 237, 238, 7, 114, 2, 2, 238, 239, 7, 113, 2, 2, 239, 240, 7, 116, 2, 2, 240, 241, 7, 118, 2, 2, 241, 4, 3, 2, 2, 2, 242, 243, 7, 48, 2, 2, 243, 6, 3, 2, 2, 2, 244, 245, 7, 118, 2, 2, 245, 246, 7, 123, 2, 2, 246, 247, 7, 114, 2, 2, 247, 248, 7, 103, 2, 2, 248, 8, 3, 2, 2, 2, 249, 250, 7, 107, 2, 2, 250, 251, 7, 117, 2, 2, 251, 10, 3, 2, 2, 2, 252, 253, 7, 85, 2, 2, 253, 254, 7, 75, 2, 2, 254, 12, 3, 2, 2, 2, 255, 256, 7, 119, 2, 2, 256, 257, 7, 112, 2, 2, 257, 258, 7, 107, 2, 2, 258, 259, 7, 118, 2, 2, 259, 14, 3, 2, 2, 2, 260, 261, 7, 113, 2, 2, 261, 262, 7, 104, 2, 2, 262, 16, 3, 2, 2, 2, 263, 264, 7, 46, 2, 2, 264, 18, 3, 2, 2, 2, 265, 266, 7, 60, 2, 2, 266, 20, 3, 2, 2, 2, 267, 268, 7, 104, 2, 2, 268, 269, 7, 99, 2, 2, 269, 270, 7, 101, 2, 2, 270, 271, 7, 118, 2, 2, 271, 272, 7, 113, 2, 2, 272, 273, 7, 116, 2, 2, 273, 22, 3, 2, 2, 2, 274, 275, 7, 113, 2, 2, 275, 276, 7, 104, 2, 2, 276, 277, 7, 104, 2, 2, 277, 278, 7, 117, 2, 2, 278, 279, 7, 103, 2, 2, 279, 280, 7, 118, 2, 2, 280, 24, 3, 2, 2, 2, 281, 282, 7, 109, 2, 2, 282, 283, 7, 105, 2, 2, 283, 26, 3, 2, 2, 2, 284, 285, 7, 111, 2, 2, 285, 28, 3, 2, 2, 2, 286, 287, 7, 117, 2, 2, 287, 30, 3, 2, 2, 2, 288, 289, 7, 67, 2, 2, 289, 32, 3, 2, 2, 2, 290, 291, 7, 77, 2, 2, 291, 34, 3, 2, 2, 2, 292, 293, 7, 111, 2, 2, 293, 294, 7, 113, 2, 2, 294, 295, 7, 110, 2, 2, 295, 36, 3, 2, 2, 2, 296, 297, 7, 101, 2, 2, 297, 298, 7, 102, 2, 2, 298, 38, 3, 2, 2, 2, 299, 300, 7, 116, 2, 2, 300, 301, 7, 99, 2, 2, 301, 302, 7, 102, 2, 2, 302, 40, 3, 2, 2, 2, 303, 304, 7, 103, 2, 2, 304, 305, 7, 112, 2, 2, 305, 306, 7, 119, 2, 2, 306, 307, 7, 111, 2, 2, 307, 42, 3, 2, 2, 2, 308, 309, 7, 63, 2, 2, 309, 44, 3, 2, 2, 2, 310, 311, 7, 35, 2, 2, 311, 46, 3, 2, 2, 2, 312, 313, 7, 63, 2, 2, 313, 314, 7, 63, 2, 2, 314, 48, 3, 2, 2, 2, 315, 316, 7, 117, 2, 2, 316, 317, 7, 118, 2, 2, 317, 318, 7, 116, 2, 2, 318, 319, 7, 119, 2, 2, 319, 320, 7, 101, 2, 2, 320, 321, 7, 118, 2, 2, 321, 50, 3, 2, 2, 2, 322, 323, 7, 107, 2, 2, 323, 324, 7, 112, 2, 2, 324, 325, 7, 106, 2, 2, 325, 326, 7, 103, 2, 2, 326, 327, 7, 116, 2, 2, 327, 328, 7, 107, 2, 2, 328, 329, 7, 118, 2, 2, 329, 330, 7, 117, 2, 2, 330, 52, 3, 2, 2, 2, 331, 332, 7, 99, 2, 2, 332, 333, 7, 101, 2, 2, 333, 334, 7, 118, 2, 2, 334, 335, 7, 113, 2, 2, 335, 336, 7, 116, 2, 2, 336, 54, 3, 2, 2, 2, 337, 338, 7, 117, 2, 2, 338, 339, 7, 101, 2, 2, 339, 340, 7, 103, 2, 2, 340, 341, 7, 112, 2, 2, 341, 342, 7, 99, 2, 2, 342, 343, 7, 116, 2, 2, 343, 344, 7, 107, 2, 2, 344, 345, 7, 113, 2, 2, 345, 56, 3, 2, 2, 2, 346, 347, 7, 99, 2, 2, 347, 348, 7, 101, 2, 2, 348, 349, 7, 118, 2, 2, 349, 350, 7, 107, 2, 2, 350, 351, 7, 113, 2, 2, 351, 352, 7, 112, 2, 2, 352, 58, 3, 2, 2, 2, 353, 354, 7, 111, 2, 2, 354, 355, 7, 113, 2, 2, 355, 356, 7, 102, 2, 2, 356, 357, 7, 107, 2, 2, 357, 358, 7, 104, 2, 2, 358, 359, 7, 107, 2, 2, 359, 360, 7, 103, 2, 2, 360, 361, 7, 116, 2, 2, 361, 60, 3, 2, 2, 2, 362, 363, 7, 103, 2, 2, 363, 364, 7, 122, 2, 2, 364, 365, 7, 118, 2, 2, 365, 366, 7, 103, 2, 2, 366, 367, 7, 112, 2, 2, 367, 368, 7, 102, 2, 2, 368, 62, 3, 2, 2, 2, 369, 370, 7, 105, 2, 2, 370, 371, 7, 110, 2, 2, 371, 372, 7, 113, 2, 2, 372, 373, 7, 100, 2, 2, 373, 374, 7, 99, 2, 2, 374, 375, 7, 110, 2, 2, 375, 64, 3, 2, 2, 2, 376, 377, 7, 110, 2, 2, 377, 378, 7, 107, 2, 2, 378, 379, 7, 117, 2, 2, 379, 380, 7, 118, 2, 2, 380, 66, 3, 2, 2, 2, 381, 382, 7, 107, 2, 2, 382, 383, 7, 112, 2, 2, 383, 384, 7, 118, 2, 2, 384, 68, 3, 2, 2, 2, 385, 386, 7, 119, 2, 2, 386, 387, 7, 107, 2, 2, 387, 388, 7, 112, 2, 2, 388, 389, 7, 118, 2, 2, 389, 70, 3, 2, 2, 2, 390, 391, 7, 104, 2, 2, 391, 392, 7, 110, 2, 2, 392, 393, 7, 113, 2, 2, 393, 394, 7, 99, 2, 2, 394, 395, 7, 118, 2, 2, 395, 72, 3, 2, 2, 2, 396, 397, 7, 100, 2, 2, 397, 398, 7, 113, 2, 2, 398, 399, 7, 113, 2, 2, 399, 400, 7, 110, 2, 2, 400, 74, 3, 2, 2, 2, 401, 402, 7, 117, 2, 2, 402, 403, 7, 118, 2, 2, 403, 404, 7, 116, 2, 2, 404, 405, 7, 107, 2, 2, 405, 406, 7, 112, 2, 2, 406, 407, 7, 105, 2, 2, 407, 76, 3, 2, 2, 2, 408, 409, 7, 103, 2, 2, 409, 410, 7, 120, 2, 2, 410, 411, 7, 103, 2, 2, 411, 412, 7, 112, 2, 2, 412, 413, 7, 118, 2, 2, 413, 78, 3, 2, 2, 2, 414, 415, 7, 107, 2, 2, 415, 416, 7, 104, 2, 2, 416, 80, 3, 2, 2, 2, 417, 418, 7, 66, 2, 2, 418, 82, 3, 2, 2, 2, 419, 420, 7, 99, 2, 2, 420, 421, 7, 117, 2, 2, 421, 84, 3, 2, 2, 2, 422, 423, 7, 116, 2, 2, 423, 424, 7, 107, 2, 2, 424, 425, 7, 117, 2, 2, 425, 426, 7, 103, 2, 2, 426, 86, 3, 2, 2, 2, 427, 428, 7, 104, 2, 2, 428, 429, 7, 99, 2, 2, 429, 430, 7, 110, 2, 2, 430, 431, 7, 110, 2, 2, 431, 88, 3, 2, 2, 2, 432, 433, 7, 103, 2, 2, 433, 434, 7, 110, 2, 2, 434, 435, 7, 99, 2, 2, 435, 436, 7, 114, 2, 2, 436, 437, 7, 117, 2, 2, 437, 438, 7, 103, 2, 2, 438, 439, 7, 102, 2, 2, 439, 90, 3, 2, 2, 2, 440, 441, 7, 103, 2, 2, 441, 442, 7, 120, 2, 2, 442, 443, 7, 103, 2, 2, 443, 444, 7, 116, 2, 2, 444, 445, 7, 123, 2, 2, 445, 92, 3, 2, 2, 2, 446, 447, 7, 120, 2, 2, 447, 448, 7, 99, 2, 2, 448, 449, 7, 116, 2, 2, 449, 94, 3, 2, 2, 2, 450, 451, 7, 117, 2, 2, 451, 452, 7, 99, 2, 2, 452, 453, 7, 111, 2, 2, 453, 454, 7, 114, 2, 2, 454, 455, 7, 110, 2, 2, 455, 456, 7, 103, 2, 2, 456, 96, 3, 2, 2, 2, 457, 458, 7, 121, 2, 2, 458, 459, 7, 107, 2, 2, 459, 460, 7, 118, 2, 2, 460, 461, 7, 106, 2, 2, 461, 98, 3, 2, 2, 2, 462, 463, 7, 109, 2, 2, 463, 464, 7, 103, 2, 2, 464, 465, 7, 103, 2, 2, 465, 466, 7, 114, 2, 2, 466, 100, 3, 2, 2, 2, 467, 468, 7, 102, 2, 2, 468, 469, 7, 103, 2, 2, 469, 470, 7, 104, 2, 2, 470, 471, 7, 99, 2, 2, 471, 472, 7, 119, 2, 2, 472, 473, 7, 110, 2, 2, 473, 474, 7, 118, 2, 2, 474, 102, 3, 2, 2, 2, 475, 476, 7, 106, 2, 2, 476, 477, 7, 99, 2, 2, 477, 478, 7, 116, 2, 2, 478, 479, 7, 102, 2, 2, 479, 104, 3, 2, 2, 2, 480, 481, 7, 116, 2, 2, 481, 482, 7, 103, 2, 2, 482, 483, 7, 111, 2, 2, 483, 484, 7, 113, 2, 2, 484, 485, 7, 120, 2, 2, 485, 486, 7, 103, 2, 2, 486, 487, 7, 97, 2, 2, 487, 488, 7, 102, 2, 2, 488, 489, 7, 103, 2, 2, 489, 490, 7, 104, 2, 2, 490, 491, 7, 99, 2, 2, 491, 492, 7, 119, 2, 2, 492, 493, 7, 110, 2, 2, 493, 494, 7, 118, 2, 2, 494, 106, 3, 2, 2, 2, 495, 496, 7, 113, 2, 2, 496, 497, 7, 112, 2, 2, 497, 108, 3, 2, 2, 2, 498, 499, 7, 102, 2, 2, 499, 500, 7, 113, 2, 2, 500, 110, 3, 2, 2, 2, 501, 502, 7, 117, 2, 2, 502, 503, 7, 103, 2, 2, 503, 504, 7, 116, 2, 2, 504, 505, 7, 107, 2, 2, 505, 506, 7, 99, 2, 2, 506, 507, 7, 110, 2, 2, 507, 112, 3, 2, 2, 2, 508, 509, 7, 113, 2, 2, 509, 510, 7, 112, 2, 2, 510, 511, 7, 103, 2, 2, 511, 512, 7, 97, 2, 2, 512, 513, 7, 113, 2, 2, 513, 514, 7, 104, 2, 2, 514, 114, 3, 2, 2, 2, 515, 516, 7, 114, 2, 2, 516, 517, 7, 99, 2, 2, 517, 518, 7, 116, 2, 2, 518, 519, 7, 99, 2, 2, 519, 520, 7, 110, 2, 2, 520, 521, 7, 110, 2, 2, 521, 522, 7, 103, 2, 2, 522, 523, 7, 110, 2, 2, 523, 116, 3, 2, 2, 2, 524, 525, 7, 121, 2, 2, 525, 526, 7, 99, 2, 2, 526, 527, 7, 107, 2, 2, 527, 528, 7, 118, 2, 2, 528, 118, 3, 2, 2, 2, 529, 530, 7, 103, 2, 2, 530, 531, 7, 111, 2, 2, 531, 532, 7, 107, 2, 2, 532, 533, 7, 118, 2, 2, 533, 120, 3, 2, 2, 2, 534, 535, 7, 101, 2, 2, 535, 536, 7, 99, 2, 2, 536, 537, 7, 110, 2, 2, 537, 538, 7, 110, 2, 2, 538, 122, 3, 2, 2, 2, 539, 540, 7, 119, 2, 2, 540, 541, 7, 112, 2, 2, 541, 542, 7, 118, 2, 2, 542, 543, 7, 107, 2, 2, 543, 544, 7, 110, 2, 2, 544, 124, 3, 2, 2, 2, 545, 546, 7, 102, 2, 2, 546, 547, 7, 103, 2, 2, 547, 548, 7, 104, 2, 2, 548, 126, 3, 2, 2, 2, 549, 550, 7, 47, 2, 2, 550, 551, 7, 64, 2, 2, 551, 128, 3, 2, 2, 2, 552, 553, 7, 103, 2, 2, 553, 554, 7, 122, 2, 2, 554, 555, 7, 114, 2, 2, 555, 556, 7, 116, 2, 2, 556, 557, 7, 103, 2, 2, 557, 558, 7, 117, 2, 2, 558, 559, 7, 117, 2, 2, 559, 560, 7, 107, 2, 2, 560, 561, 7, 113, 2, 2, 561, 562, 7, 112, 2, 2, 562, 130, 3, 2, 2, 2, 563, 564, 7, 119, 2, 2, 564, 565, 7, 112, 2, 2, 565, 566, 7, 102, 2, 2, 566, 567, 7, 103, 2, 2, 567, 568, 7, 104, 2, 2, 568, 569, 7, 107, 2, 2, 569, 570, 7, 112, 2, 2, 570, 571, 7, 103, 2, 2, 571, 572, 7, 102, 2, 2, 572, 132, 3, 2, 2, 2, 573, 574, 7, 103, 2, 2, 574, 575, 7, 122, 2, 2, 575, 576, 7, 118, 2, 2, 576, 577, 7, 103, 2, 2, 577, 578, 7, 116, 2, 2, 578, 579, 7, 112, 2, 2, 579, 580, 7, 99, 2, 2, 580, 581, 7, 110, 2, 2, 581, 134, 3, 2, 2, 2, 582, 583, 7, 113, 2, 2, 583, 584, 7, 112, 2, 2, 584, 585, 7, 110, 2, 2, 585, 586, 7, 123, 2, 2, 586, 136, 3, 2, 2, 2, 587, 588, 7, 101, 2, 2, 588, 589, 7, 113, 2, 2, 589, 590, 7, 120, 2, 2, 590, 591, 7, 103, 2, 2, 591, 592, 7, 116, 2, 2, 592, 138, 3, 2, 2, 2, 593, 594, 7, 116, 2, 2, 594, 595, 7, 103, 2, 2, 595, 596, 7, 101, 2, 2, 596, 597, 7, 113, 2, 2, 597, 598, 7, 116, 2, 2, 598, 599, 7, 102, 2, 2, 599, 140, 3, 2, 2, 2, 600, 601, 7, 116, 2, 2, 601, 602, 7, 99, 2, 2, 602, 603, 7, 112, 2, 2, 603, 604, 7, 105, 2, 2, 604, 605, 7, 103, 2, 2, 605, 142, 3, 2, 2, 2, 606, 607, 7, 65, 2, 2, 607, 144, 3, 2, 2, 2, 608, 609, 7, 63, 2, 2, 609, 610, 7, 64, 2, 2, 610, 146, 3, 2, 2, 2, 611, 612, 7, 113, 2, 2, 612, 613, 7, 116, 2, 2, 613, 148, 3, 2, 2, 2, 614, 615, 7, 99, 2, 2, 615, 616, 7, 112, 2, 2, 616, 617, 7, 102, 2, 2, 617, 150, 3, 2, 2, 2, 618, 619, 7, 112, 2, 2, 619, 620, 7, 113, 2, 2, 620, 621, 7, 118, 2, 2, 621, 152, 3, 2, 2, 2, 622, 623, 7, 35, 2, 2, 623, 624, 7, 63, 2, 2, 624, 154, 3, 2, 2, 2, 625, 626, 7, 62, 2, 2, 626, 156, 3, 2, 2, 2, 627, 628, 7, 62, 2, 2, 628, 629, 7, 63, 2, 2, 629, 158, 3, 2, 2, 2, 630, 631, 7, 64, 2, 2, 631, 160, 3, 2, 2, 2, 632, 633, 7, 64, 2, 2, 633, 634, 7, 63, 2, 2, 634, 162, 3, 2, 2, 2, 635, 636, 7, 107, 2, 2, 636, 637, 7, 112, 2, 2, 637, 164, 3, 2, 2, 2, 638, 639, 7, 45, 2, 2, 639, 166, 3, 2, 2, 2, 640, 641, 7, 47, 2, 2, 641, 168, 3, 2, 2, 2, 642, 643, 7, 44, 2, 2, 643, 170, 3, 2, 2, 2, 644, 645, 7, 49, 2, 2, 645, 172, 3, 2, 2, 2, 646, 647, 7, 39, 2, 2, 647, 174, 3, 2, 2, 2, 648, 649, 7, 107, 2, 2, 649, 650, 7, 118, 2, 2, 650, 176, 3, 2, 2, 2, 651, 652, 7, 48, 2, 2, 652, 653, 7, 48, 2, 2, 653, 178, 3, 2, 2, 2, 654, 655, 6, 90, 2, 2, 655, 667, 5, 191, 96, 2, 656, 658, 7, 15, 2, 2, 657, 656, 3, 2, 2, 2, 657, 658, 3, 2, 2, 2, 658, 659, 3, 2, 2, 2, 659, 662, 7, 12, 2, 2, 660, 662, 4, 14, 15, 2, 661, 657, 3, 2, 2, 2, 661, 660, 3, 2, 2, 2, 662, 664, 3, 2, 2, 2, 663, 665, 5, 191, 96, 2, 664, 663, 3, 2, 2, 2, 664, 665, 3, 2, 2, 2, 665, 667, 3, 2, 2, 2, 666, 654, 3, 2, 2, 2, 666, 661, 3, 2, 2, 2, 667, 668, 3, 2, 2, 2, 668, 669, 8, 90, 2, 2, 669, 180, 3, 2, 2, 2, 670, 671, 7, 93, 2, 2, 671, 672, 8, 91, 3, 2, 672, 182, 3, 2, 2, 2, 673, 674, 7, 95, 2, 2, 674, 675, 8, 92, 4, 2, 675, 184, 3, 2, 2, 2, 676, 677, 7, 42, 2, 2, 677, 678, 8, 93, 5, 2, 678, 186, 3, 2, 2, 2, 679, 680, 7, 43, 2, 2, 680, 681, 8, 94, 6, 2, 681, 188, 3, 2, 2, 2, 682, 685, 5, 191, 96, 2, 683, 685, 5, 193, 97, 2, 684, 682, 3, 2, 2, 2, 684, 683, 3, 2, 2, 2, 685, 686, 3, 2, 2, 2, 686, 687, 8, 95, 7, 2, 687, 190, 3, 2, 2, 2, 688, 690, 9, 2, 2, 2, 689, 688, 3, 2, 2, 2, 690, 691, 3, 2, 2, 2, 691, 689, 3, 2, 2, 2, 691, 692, 3, 2, 2, 2, 692, 192, 3, 2, 2, 2, 693, 695, 7, 94, 2, 2, 694, 696, 5, 191, 96, 2, 695, 694, 3, 2, 2, 2, 695, 696, 3, 2, 2, 2, 696, 698, 3, 2, 2, 2, 697, 699, 7, 15, 2, 2, 698, 697, 3, 2, 2, 2, 698, 699, 3, 2, 2, 2, 699, 700, 3, 2, 2, 2, 700, 701, 7, 12, 2, 2, 701, 194, 3, 2, 2, 2, 702, 704, 7, 15, 2, 2, 703, 702, 3, 2, 2, 2, 703, 704, 3, 2, 2, 2, 704, 705, 3, 2, 2, 2, 705, 706, 7, 12, 2, 2, 706, 196, 3, 2, 2, 2, 707, 708, 7, 49, 2, 2, 708, 709, 7, 44, 2, 2, 709, 713, 3, 2, 2, 2, 710, 712, 11, 2, 2, 2, 711, 710, 3, 2, 2, 2, 712, 715, 3, 2, 2, 2, 713, 714, 3, 2, 2, 2, 713, 711, 3, 2, 2, 2, 714, 716, 3, 2, 2, 2, 715, 713, 3, 2, 2, 2, 716, 717, 7, 44, 2, 2, 717, 718, 7, 49, 2, 2, 718, 719, 3, 2, 2, 2, 719, 720, 8, 99, 7, 2, 720, 198, 3, 2, 2, 2, 721, 725, 7, 37, 2, 2, 722, 724, 10, 3, 2, 2, 723, 722, 3, 2, 2, 2, 724, 727, 3, 2, 2, 2, 725, 723, 3, 2, 2, 2, 725, 726, 3, 2, 2, 2, 726, 728, 3, 2, 2, 2, 727, 725, 3, 2, 2, 2, 728, 729, 8, 100, 7, 2, 729, 200, 3, 2, 2, 2, 730, 733, 5, 203, 102, 2, 731, 733, 5, 209, 105, 2, 732, 730, 3, 2, 2, 2, 732, 731, 3, 2, 2, 2, 733, 202, 3, 2, 2, 2, 734, 738, 7, 36, 2, 2, 735, 737, 5, 205, 103, 2, 736, 735, 3, 2, 2, 2, 737, 740, 3, 2, 2, 2, 738, 736, 3, 2, 2, 2, 738, 739, 3, 2, 2, 2, 739, 741, 3, 2, 2, 2, 740, 738, 3, 2, 2, 2, 741, 751, 7, 36, 2, 2, 742, 746, 7, 41, 2, 2, 743, 745, 5, 205, 103, 2, 744, 743, 3, 2, 2, 2, 745, 748, 3, 2, 2, 2, 746, 744, 3, 2, 2, 2, 746, 747, 3, 2, 2, 2, 747, 749, 3, 2, 2, 2, 748, 746, 3, 2, 2, 2, 749, 751, 7, 41, 2, 2, 750, 734, 3, 2, 2, 2, 750, 742, 3, 2, 2, 2, 751, 204, 3, 2, 2, 2, 752, 755, 5, 207, 104, 2, 753, 755, 5, 215, 108, 2, 754, 752, 3, 2, 2, 2, 754, 753, 3, 2, 2, 2, 755, 206, 3, 2, 2, 2, 756, 757, 10, 4, 2, 2, 757, 208, 3, 2, 2, 2, 758, 759, 7, 36, 2, 2, 759, 760, 7, 36, 2, 2, 760, 761, 7, 36, 2, 2, 761, 765, 3, 2, 2, 2, 762, 764, 5, 211, 106, 2, 763, 762, 3, 2, 2, 2, 764, 767, 3, 2, 2, 2, 765, 763, 3, 2, 2, 2, 765, 766, 3, 2, 2, 2, 766, 768, 3, 2, 2, 2, 767, 765, 3, 2, 2, 2, 768, 769, 7, 36, 2, 2, 769, 770, 7, 36, 2, 2, 770, 785, 7, 36, 2, 2, 771, 772, 7, 41, 2, 2, 772, 773, 7, 41, 2, 2, 773, 774, 7, 41, 2, 2, 774, 778, 3, 2, 2, 2, 775, 777, 5, 211, 106, 2, 776, 775, 3, 2, 2, 2, 777, 780, 3, 2, 2, 2, 778, 776, 3, 2, 2, 2, 778, 779, 3, 2, 2, 2, 779, 781, 3, 2, 2, 2, 780, 778, 3, 2, 2, 2, 781, 782, 7, 41, 2, 2, 782, 783, 7, 41, 2, 2, 783, 785, 7, 41, 2, 2, 784, 758, 3, 2, 2, 2, 784, 771, 3, 2, 2, 2, 785, 210, 3, 2, 2, 2, 786, 789, 5, 213, 107, 2, 787, 789, 5, 215, 108, 2, 788, 786, 3, 2, 2, 2, 788, 787, 3, 2, 2, 2, 789, 212, 3, 2, 2, 2, 790, 791, 10, 5, 2, 2, 791, 214, 3, 2, 2, 2, 792, 793, 7, 94, 2, 2, 793, 797, 11, 2, 2, 2, 794, 795, 7, 94, 2, 2, 795, 797, 5, 195, 98, 2, 796, 792, 3, 2, 2, 2, 796, 794, 3, 2, 2, 2, 797, 216, 3, 2, 2, 2, 798, 800, 9, 6, 2, 2, 799, 798, 3, 2, 2, 2, 799, 800, 3, 2, 2, 2, 800, 804, 3, 2, 2, 2, 801, 803, 5, 231, 116, 2, 802, 801, 3, 2, 2, 2, 803, 806, 3, 2, 2, 2, 804, 802, 3, 2, 2, 2, 804, 805, 3, 2, 2, 2, 805, 807, 3, 2, 2, 2, 806, 804, 3, 2, 2, 2, 807, 809, 7, 48, 2, 2, 808, 810, 5, 231, 116, 2, 809, 808, 3, 2, 2, 2, 810, 811, 3, 2, 2, 2, 811, 809, 3, 2, 2, 2, 811, 812, 3, 2, 2, 2, 812, 822, 3, 2, 2, 2, 813, 815, 9, 7, 2, 2, 814, 816, 9, 6, 2, 2, 815, 814, 3, 2, 2, 2, 815, 816, 3, 2, 2, 2, 816, 818, 3, 2, 2, 2, 817, 819, 5, 231, 116, 2, 818, 817, 3, 2, 2, 2, 819, 820, 3, 2, 2, 2, 820, 818, 3, 2, 2, 2, 820, 821, 3, 2, 2, 2, 821, 823, 3, 2, 2, 2, 822, 813, 3, 2, 2, 2, 822, 823, 3, 2, 2, 2, 823, 218, 3, 2, 2, 2, 824, 826, 5, 231, 116, 2, 825, 824, 3, 2, 2, 2, 826, 827, 3, 2, 2, 2, 827, 825, 3, 2, 2, 2, 827, 828, 3, 2, 2, 2, 828, 220, 3, 2, 2, 2, 829, 830, 7, 50, 2, 2, 830, 831, 7, 122, 2, 2, 831, 833, 3, 2, 2, 2, 832, 834, 5, 233, 117, 2, 833, 832, 3, 2, 2, 2, 834, 835, 3, 2, 2, 2, 835, 833, 3, 2, 2, 2, 835, 836, 3, 2, 2, 2, 836, 222, 3, 2, 2, 2, 837, 839, 7, 47, 2, 2, 838, 840, 5, 231, 116, 2, 839, 838, 3, 2, 2, 2, 840, 841, 3, 2, 2, 2, 841, 839, 3, 2, 2, 2, 841, 842, 3, 2, 2, 2, 842, 224, 3, 2, 2, 2, 843, 844, 7, 118, 2, 2, 844, 845, 7, 116, 2, 2, 845, 846, 7, 119, 2, 2, 846, 853, 7, 103, 2, 2, 847, 848, 7, 104, 2, 2, 848, 849, 7, 99, 2, 2, 849, 850, 7, 110, 2, 2, 850, 851, 7, 117, 2, 2, 851, 853, 7, 103, 2, 2, 852, 843, 3, 2, 2, 2, 852, 847, 3, 2, 2, 2, 853, 226, 3, 2, 2, 2, 854, 858, 9, 8, 2, 2, 855, 857, 9, 9, 2, 2, 856, 855, 3, 2, 2, 2, 857, 860, 3, 2, 2, 2, 858, 856, 3, 2, 2, 2, 858, 859, 3, 2, 2, 2, 859, 869, 3, 2, 2, 2, 860, 858, 3, 2, 2, 2, 861, 863, 7, 126, 2, 2, 862, 864, 10, 10, 2, 2, 863, 862, 3, 2, 2, 2, 864, 865, 3, 2, 2, 2, 865, 863, 3, 2, 2, 2, 865, 866, 3, 2, 2, 2, 866, 867, 3, 2, 2, 2, 867, 869, 7, 126, 2, 2, 868, 854, 3, 2, 2, 2, 868, 861, 3, 2, 2, 2, 869, 228, 3, 2, 2, 2, 870, 871, 10, 10, 2, 2, 871, 230, 3, 2, 2, 2, 872, 873, 9, 11, 2, 2, 873, 232, 3, 2, 2, 2, 874, 875, 9, 12, 2, 2, 875, 234, 3, 2, 2, 2, 37, 2, 657, 661, 664, 666, 684, 691, 695, 698, 703, 713, 725, 732, 738, 746, 750, 754, 765, 778, 784, 788, 796, 799, 804, 811, 815, 820, 822, 827, 835, 841, 852, 858, 865, 868, 8, 3, 90, 2, 3, 91, 3, 3, 92, 4, 3, 93, 5, 3, 94, 6, 8, 2, 2] \ No newline at end of file diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.py b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.py new file mode 100644 index 00000000..68a65706 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.py @@ -0,0 +1,757 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# Generated from OpenSCENARIO2.g4 by ANTLR 4.7.2 +from antlr4 import * +from io import StringIO +from typing.io import TextIO +import sys + + +from antlr4.Token import CommonToken +import re +import importlib +# Allow languages to extend the lexer and parser, by loading the parser dynamically +module_path = __name__[:-5] +language_name = __name__.split('.')[-1] +language_name = language_name[:-5] # Remove Lexer from name +LanguageParser = getattr(importlib.import_module('{}Parser'.format(module_path)), '{}Parser'.format(language_name)) + + +def serializedATN(): + with StringIO() as buf: + buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2i") + buf.write("\u036c\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7") + buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r") + buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23") + buf.write("\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30") + buf.write("\4\31\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36") + buf.write("\t\36\4\37\t\37\4 \t \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%") + buf.write("\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t,\4-\t-\4.") + buf.write("\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64") + buf.write("\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:") + buf.write("\4;\t;\4<\t<\4=\t=\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\t") + buf.write("C\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I\tI\4J\tJ\4K\tK\4L\t") + buf.write("L\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT\4U\t") + buf.write("U\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4") + buf.write("^\t^\4_\t_\4`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4") + buf.write("g\tg\4h\th\4i\ti\4j\tj\4k\tk\4l\tl\4m\tm\4n\tn\4o\to\4") + buf.write("p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\3\2\3\2\3\2\3\2\3\2") + buf.write("\3\2\3\2\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\5\3\5\3\5\3\6\3") + buf.write("\6\3\6\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\t\3\t\3\n\3\n") + buf.write("\3\13\3\13\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3") + buf.write("\f\3\f\3\f\3\r\3\r\3\r\3\16\3\16\3\17\3\17\3\20\3\20\3") + buf.write("\21\3\21\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\24\3\24") + buf.write("\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\27\3\27") + buf.write("\3\30\3\30\3\30\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\32") + buf.write("\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\33") + buf.write("\3\33\3\33\3\33\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34") + buf.write("\3\34\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\36\3\36\3\36") + buf.write("\3\36\3\36\3\36\3\36\3\36\3\36\3\37\3\37\3\37\3\37\3\37") + buf.write("\3\37\3\37\3 \3 \3 \3 \3 \3 \3 \3!\3!\3!\3!\3!\3\"\3\"") + buf.write("\3\"\3\"\3#\3#\3#\3#\3#\3$\3$\3$\3$\3$\3$\3%\3%\3%\3%") + buf.write("\3%\3&\3&\3&\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3\'\3\'\3(\3") + buf.write("(\3(\3)\3)\3*\3*\3*\3+\3+\3+\3+\3+\3,\3,\3,\3,\3,\3-\3") + buf.write("-\3-\3-\3-\3-\3-\3-\3.\3.\3.\3.\3.\3.\3/\3/\3/\3/\3\60") + buf.write("\3\60\3\60\3\60\3\60\3\60\3\60\3\61\3\61\3\61\3\61\3\61") + buf.write("\3\62\3\62\3\62\3\62\3\62\3\63\3\63\3\63\3\63\3\63\3\63") + buf.write("\3\63\3\63\3\64\3\64\3\64\3\64\3\64\3\65\3\65\3\65\3\65") + buf.write("\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\65") + buf.write("\3\66\3\66\3\66\3\67\3\67\3\67\38\38\38\38\38\38\38\3") + buf.write("9\39\39\39\39\39\39\3:\3:\3:\3:\3:\3:\3:\3:\3:\3;\3;\3") + buf.write(";\3;\3;\3<\3<\3<\3<\3<\3=\3=\3=\3=\3=\3>\3>\3>\3>\3>\3") + buf.write(">\3?\3?\3?\3?\3@\3@\3@\3A\3A\3A\3A\3A\3A\3A\3A\3A\3A\3") + buf.write("A\3B\3B\3B\3B\3B\3B\3B\3B\3B\3B\3C\3C\3C\3C\3C\3C\3C\3") + buf.write("C\3C\3D\3D\3D\3D\3D\3E\3E\3E\3E\3E\3E\3F\3F\3F\3F\3F\3") + buf.write("F\3F\3G\3G\3G\3G\3G\3G\3H\3H\3I\3I\3I\3J\3J\3J\3K\3K\3") + buf.write("K\3K\3L\3L\3L\3L\3M\3M\3M\3N\3N\3O\3O\3O\3P\3P\3Q\3Q\3") + buf.write("Q\3R\3R\3R\3S\3S\3T\3T\3U\3U\3V\3V\3W\3W\3X\3X\3X\3Y\3") + buf.write("Y\3Y\3Z\3Z\3Z\5Z\u0292\nZ\3Z\3Z\5Z\u0296\nZ\3Z\5Z\u0299") + buf.write("\nZ\5Z\u029b\nZ\3Z\3Z\3[\3[\3[\3\\\3\\\3\\\3]\3]\3]\3") + buf.write("^\3^\3^\3_\3_\5_\u02ad\n_\3_\3_\3`\6`\u02b2\n`\r`\16`") + buf.write("\u02b3\3a\3a\5a\u02b8\na\3a\5a\u02bb\na\3a\3a\3b\5b\u02c0") + buf.write("\nb\3b\3b\3c\3c\3c\3c\7c\u02c8\nc\fc\16c\u02cb\13c\3c") + buf.write("\3c\3c\3c\3c\3d\3d\7d\u02d4\nd\fd\16d\u02d7\13d\3d\3d") + buf.write("\3e\3e\5e\u02dd\ne\3f\3f\7f\u02e1\nf\ff\16f\u02e4\13f") + buf.write("\3f\3f\3f\7f\u02e9\nf\ff\16f\u02ec\13f\3f\5f\u02ef\nf") + buf.write("\3g\3g\5g\u02f3\ng\3h\3h\3i\3i\3i\3i\3i\7i\u02fc\ni\f") + buf.write("i\16i\u02ff\13i\3i\3i\3i\3i\3i\3i\3i\3i\7i\u0309\ni\f") + buf.write("i\16i\u030c\13i\3i\3i\3i\5i\u0311\ni\3j\3j\5j\u0315\n") + buf.write("j\3k\3k\3l\3l\3l\3l\5l\u031d\nl\3m\5m\u0320\nm\3m\7m\u0323") + buf.write("\nm\fm\16m\u0326\13m\3m\3m\6m\u032a\nm\rm\16m\u032b\3") + buf.write("m\3m\5m\u0330\nm\3m\6m\u0333\nm\rm\16m\u0334\5m\u0337") + buf.write("\nm\3n\6n\u033a\nn\rn\16n\u033b\3o\3o\3o\3o\6o\u0342\n") + buf.write("o\ro\16o\u0343\3p\3p\6p\u0348\np\rp\16p\u0349\3q\3q\3") + buf.write("q\3q\3q\3q\3q\3q\3q\5q\u0355\nq\3r\3r\7r\u0359\nr\fr\16") + buf.write("r\u035c\13r\3r\3r\6r\u0360\nr\rr\16r\u0361\3r\5r\u0365") + buf.write("\nr\3s\3s\3t\3t\3u\3u\3\u02c9\2v\3\3\5\4\7\5\t\6\13\7") + buf.write("\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21") + buf.write("!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33\65\34\67") + buf.write("\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61") + buf.write("a\62c\63e\64g\65i\66k\67m8o9q:s;u{?}@\177A\u0081") + buf.write("B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091") + buf.write("J\u0093K\u0095L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1") + buf.write("R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00afY\u00b1") + buf.write("Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bf\2\u00c1") + buf.write("\2\u00c3\2\u00c5a\u00c7b\u00c9c\u00cb\2\u00cd\2\u00cf") + buf.write("\2\u00d1\2\u00d3\2\u00d5\2\u00d7\2\u00d9d\u00dbe\u00dd") + buf.write("f\u00dfg\u00e1h\u00e3i\u00e5\2\u00e7\2\u00e9\2\3\2\r\4") + buf.write("\2\13\13\"\"\4\2\f\f\16\17\7\2\f\f\17\17$$))^^\3\2^^\4") + buf.write("\2--//\4\2GGgg\4\2C\\c|\6\2\62;C\\aac|\3\2~~\3\2\62;\5") + buf.write("\2\62;CHch\2\u0380\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2") + buf.write("\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21") + buf.write("\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3") + buf.write("\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2") + buf.write("\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2") + buf.write("\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2") + buf.write("\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2") + buf.write("\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3") + buf.write("\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q") + buf.write("\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2") + buf.write("[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2") + buf.write("\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2") + buf.write("\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2") + buf.write("\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2") + buf.write("\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087") + buf.write("\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2") + buf.write("\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095") + buf.write("\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2") + buf.write("\2\2\u009d\3\2\2\2\2\u009f\3\2\2\2\2\u00a1\3\2\2\2\2\u00a3") + buf.write("\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2") + buf.write("\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1") + buf.write("\3\2\2\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2") + buf.write("\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2\2\2\u00bd\3\2\2\2\2\u00c5") + buf.write("\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00d9\3\2\2") + buf.write("\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df\3\2\2\2\2\u00e1") + buf.write("\3\2\2\2\2\u00e3\3\2\2\2\3\u00eb\3\2\2\2\5\u00f2\3\2\2") + buf.write("\2\7\u00f4\3\2\2\2\t\u00f9\3\2\2\2\13\u00fc\3\2\2\2\r") + buf.write("\u00ff\3\2\2\2\17\u0104\3\2\2\2\21\u0107\3\2\2\2\23\u0109") + buf.write("\3\2\2\2\25\u010b\3\2\2\2\27\u0112\3\2\2\2\31\u0119\3") + buf.write("\2\2\2\33\u011c\3\2\2\2\35\u011e\3\2\2\2\37\u0120\3\2") + buf.write("\2\2!\u0122\3\2\2\2#\u0124\3\2\2\2%\u0128\3\2\2\2\'\u012b") + buf.write("\3\2\2\2)\u012f\3\2\2\2+\u0134\3\2\2\2-\u0136\3\2\2\2") + buf.write("/\u0138\3\2\2\2\61\u013b\3\2\2\2\63\u0142\3\2\2\2\65\u014b") + buf.write("\3\2\2\2\67\u0151\3\2\2\29\u015a\3\2\2\2;\u0161\3\2\2") + buf.write("\2=\u016a\3\2\2\2?\u0171\3\2\2\2A\u0178\3\2\2\2C\u017d") + buf.write("\3\2\2\2E\u0181\3\2\2\2G\u0186\3\2\2\2I\u018c\3\2\2\2") + buf.write("K\u0191\3\2\2\2M\u0198\3\2\2\2O\u019e\3\2\2\2Q\u01a1\3") + buf.write("\2\2\2S\u01a3\3\2\2\2U\u01a6\3\2\2\2W\u01ab\3\2\2\2Y\u01b0") + buf.write("\3\2\2\2[\u01b8\3\2\2\2]\u01be\3\2\2\2_\u01c2\3\2\2\2") + buf.write("a\u01c9\3\2\2\2c\u01ce\3\2\2\2e\u01d3\3\2\2\2g\u01db\3") + buf.write("\2\2\2i\u01e0\3\2\2\2k\u01ef\3\2\2\2m\u01f2\3\2\2\2o\u01f5") + buf.write("\3\2\2\2q\u01fc\3\2\2\2s\u0203\3\2\2\2u\u020c\3\2\2\2") + buf.write("w\u0211\3\2\2\2y\u0216\3\2\2\2{\u021b\3\2\2\2}\u0221\3") + buf.write("\2\2\2\177\u0225\3\2\2\2\u0081\u0228\3\2\2\2\u0083\u0233") + buf.write("\3\2\2\2\u0085\u023d\3\2\2\2\u0087\u0246\3\2\2\2\u0089") + buf.write("\u024b\3\2\2\2\u008b\u0251\3\2\2\2\u008d\u0258\3\2\2\2") + buf.write("\u008f\u025e\3\2\2\2\u0091\u0260\3\2\2\2\u0093\u0263\3") + buf.write("\2\2\2\u0095\u0266\3\2\2\2\u0097\u026a\3\2\2\2\u0099\u026e") + buf.write("\3\2\2\2\u009b\u0271\3\2\2\2\u009d\u0273\3\2\2\2\u009f") + buf.write("\u0276\3\2\2\2\u00a1\u0278\3\2\2\2\u00a3\u027b\3\2\2\2") + buf.write("\u00a5\u027e\3\2\2\2\u00a7\u0280\3\2\2\2\u00a9\u0282\3") + buf.write("\2\2\2\u00ab\u0284\3\2\2\2\u00ad\u0286\3\2\2\2\u00af\u0288") + buf.write("\3\2\2\2\u00b1\u028b\3\2\2\2\u00b3\u029a\3\2\2\2\u00b5") + buf.write("\u029e\3\2\2\2\u00b7\u02a1\3\2\2\2\u00b9\u02a4\3\2\2\2") + buf.write("\u00bb\u02a7\3\2\2\2\u00bd\u02ac\3\2\2\2\u00bf\u02b1\3") + buf.write("\2\2\2\u00c1\u02b5\3\2\2\2\u00c3\u02bf\3\2\2\2\u00c5\u02c3") + buf.write("\3\2\2\2\u00c7\u02d1\3\2\2\2\u00c9\u02dc\3\2\2\2\u00cb") + buf.write("\u02ee\3\2\2\2\u00cd\u02f2\3\2\2\2\u00cf\u02f4\3\2\2\2") + buf.write("\u00d1\u0310\3\2\2\2\u00d3\u0314\3\2\2\2\u00d5\u0316\3") + buf.write("\2\2\2\u00d7\u031c\3\2\2\2\u00d9\u031f\3\2\2\2\u00db\u0339") + buf.write("\3\2\2\2\u00dd\u033d\3\2\2\2\u00df\u0345\3\2\2\2\u00e1") + buf.write("\u0354\3\2\2\2\u00e3\u0364\3\2\2\2\u00e5\u0366\3\2\2\2") + buf.write("\u00e7\u0368\3\2\2\2\u00e9\u036a\3\2\2\2\u00eb\u00ec\7") + buf.write("k\2\2\u00ec\u00ed\7o\2\2\u00ed\u00ee\7r\2\2\u00ee\u00ef") + buf.write("\7q\2\2\u00ef\u00f0\7t\2\2\u00f0\u00f1\7v\2\2\u00f1\4") + buf.write("\3\2\2\2\u00f2\u00f3\7\60\2\2\u00f3\6\3\2\2\2\u00f4\u00f5") + buf.write("\7v\2\2\u00f5\u00f6\7{\2\2\u00f6\u00f7\7r\2\2\u00f7\u00f8") + buf.write("\7g\2\2\u00f8\b\3\2\2\2\u00f9\u00fa\7k\2\2\u00fa\u00fb") + buf.write("\7u\2\2\u00fb\n\3\2\2\2\u00fc\u00fd\7U\2\2\u00fd\u00fe") + buf.write("\7K\2\2\u00fe\f\3\2\2\2\u00ff\u0100\7w\2\2\u0100\u0101") + buf.write("\7p\2\2\u0101\u0102\7k\2\2\u0102\u0103\7v\2\2\u0103\16") + buf.write("\3\2\2\2\u0104\u0105\7q\2\2\u0105\u0106\7h\2\2\u0106\20") + buf.write("\3\2\2\2\u0107\u0108\7.\2\2\u0108\22\3\2\2\2\u0109\u010a") + buf.write("\7<\2\2\u010a\24\3\2\2\2\u010b\u010c\7h\2\2\u010c\u010d") + buf.write("\7c\2\2\u010d\u010e\7e\2\2\u010e\u010f\7v\2\2\u010f\u0110") + buf.write("\7q\2\2\u0110\u0111\7t\2\2\u0111\26\3\2\2\2\u0112\u0113") + buf.write("\7q\2\2\u0113\u0114\7h\2\2\u0114\u0115\7h\2\2\u0115\u0116") + buf.write("\7u\2\2\u0116\u0117\7g\2\2\u0117\u0118\7v\2\2\u0118\30") + buf.write("\3\2\2\2\u0119\u011a\7m\2\2\u011a\u011b\7i\2\2\u011b\32") + buf.write("\3\2\2\2\u011c\u011d\7o\2\2\u011d\34\3\2\2\2\u011e\u011f") + buf.write("\7u\2\2\u011f\36\3\2\2\2\u0120\u0121\7C\2\2\u0121 \3\2") + buf.write("\2\2\u0122\u0123\7M\2\2\u0123\"\3\2\2\2\u0124\u0125\7") + buf.write("o\2\2\u0125\u0126\7q\2\2\u0126\u0127\7n\2\2\u0127$\3\2") + buf.write("\2\2\u0128\u0129\7e\2\2\u0129\u012a\7f\2\2\u012a&\3\2") + buf.write("\2\2\u012b\u012c\7t\2\2\u012c\u012d\7c\2\2\u012d\u012e") + buf.write("\7f\2\2\u012e(\3\2\2\2\u012f\u0130\7g\2\2\u0130\u0131") + buf.write("\7p\2\2\u0131\u0132\7w\2\2\u0132\u0133\7o\2\2\u0133*\3") + buf.write("\2\2\2\u0134\u0135\7?\2\2\u0135,\3\2\2\2\u0136\u0137\7") + buf.write("#\2\2\u0137.\3\2\2\2\u0138\u0139\7?\2\2\u0139\u013a\7") + buf.write("?\2\2\u013a\60\3\2\2\2\u013b\u013c\7u\2\2\u013c\u013d") + buf.write("\7v\2\2\u013d\u013e\7t\2\2\u013e\u013f\7w\2\2\u013f\u0140") + buf.write("\7e\2\2\u0140\u0141\7v\2\2\u0141\62\3\2\2\2\u0142\u0143") + buf.write("\7k\2\2\u0143\u0144\7p\2\2\u0144\u0145\7j\2\2\u0145\u0146") + buf.write("\7g\2\2\u0146\u0147\7t\2\2\u0147\u0148\7k\2\2\u0148\u0149") + buf.write("\7v\2\2\u0149\u014a\7u\2\2\u014a\64\3\2\2\2\u014b\u014c") + buf.write("\7c\2\2\u014c\u014d\7e\2\2\u014d\u014e\7v\2\2\u014e\u014f") + buf.write("\7q\2\2\u014f\u0150\7t\2\2\u0150\66\3\2\2\2\u0151\u0152") + buf.write("\7u\2\2\u0152\u0153\7e\2\2\u0153\u0154\7g\2\2\u0154\u0155") + buf.write("\7p\2\2\u0155\u0156\7c\2\2\u0156\u0157\7t\2\2\u0157\u0158") + buf.write("\7k\2\2\u0158\u0159\7q\2\2\u01598\3\2\2\2\u015a\u015b") + buf.write("\7c\2\2\u015b\u015c\7e\2\2\u015c\u015d\7v\2\2\u015d\u015e") + buf.write("\7k\2\2\u015e\u015f\7q\2\2\u015f\u0160\7p\2\2\u0160:\3") + buf.write("\2\2\2\u0161\u0162\7o\2\2\u0162\u0163\7q\2\2\u0163\u0164") + buf.write("\7f\2\2\u0164\u0165\7k\2\2\u0165\u0166\7h\2\2\u0166\u0167") + buf.write("\7k\2\2\u0167\u0168\7g\2\2\u0168\u0169\7t\2\2\u0169<\3") + buf.write("\2\2\2\u016a\u016b\7g\2\2\u016b\u016c\7z\2\2\u016c\u016d") + buf.write("\7v\2\2\u016d\u016e\7g\2\2\u016e\u016f\7p\2\2\u016f\u0170") + buf.write("\7f\2\2\u0170>\3\2\2\2\u0171\u0172\7i\2\2\u0172\u0173") + buf.write("\7n\2\2\u0173\u0174\7q\2\2\u0174\u0175\7d\2\2\u0175\u0176") + buf.write("\7c\2\2\u0176\u0177\7n\2\2\u0177@\3\2\2\2\u0178\u0179") + buf.write("\7n\2\2\u0179\u017a\7k\2\2\u017a\u017b\7u\2\2\u017b\u017c") + buf.write("\7v\2\2\u017cB\3\2\2\2\u017d\u017e\7k\2\2\u017e\u017f") + buf.write("\7p\2\2\u017f\u0180\7v\2\2\u0180D\3\2\2\2\u0181\u0182") + buf.write("\7w\2\2\u0182\u0183\7k\2\2\u0183\u0184\7p\2\2\u0184\u0185") + buf.write("\7v\2\2\u0185F\3\2\2\2\u0186\u0187\7h\2\2\u0187\u0188") + buf.write("\7n\2\2\u0188\u0189\7q\2\2\u0189\u018a\7c\2\2\u018a\u018b") + buf.write("\7v\2\2\u018bH\3\2\2\2\u018c\u018d\7d\2\2\u018d\u018e") + buf.write("\7q\2\2\u018e\u018f\7q\2\2\u018f\u0190\7n\2\2\u0190J\3") + buf.write("\2\2\2\u0191\u0192\7u\2\2\u0192\u0193\7v\2\2\u0193\u0194") + buf.write("\7t\2\2\u0194\u0195\7k\2\2\u0195\u0196\7p\2\2\u0196\u0197") + buf.write("\7i\2\2\u0197L\3\2\2\2\u0198\u0199\7g\2\2\u0199\u019a") + buf.write("\7x\2\2\u019a\u019b\7g\2\2\u019b\u019c\7p\2\2\u019c\u019d") + buf.write("\7v\2\2\u019dN\3\2\2\2\u019e\u019f\7k\2\2\u019f\u01a0") + buf.write("\7h\2\2\u01a0P\3\2\2\2\u01a1\u01a2\7B\2\2\u01a2R\3\2\2") + buf.write("\2\u01a3\u01a4\7c\2\2\u01a4\u01a5\7u\2\2\u01a5T\3\2\2") + buf.write("\2\u01a6\u01a7\7t\2\2\u01a7\u01a8\7k\2\2\u01a8\u01a9\7") + buf.write("u\2\2\u01a9\u01aa\7g\2\2\u01aaV\3\2\2\2\u01ab\u01ac\7") + buf.write("h\2\2\u01ac\u01ad\7c\2\2\u01ad\u01ae\7n\2\2\u01ae\u01af") + buf.write("\7n\2\2\u01afX\3\2\2\2\u01b0\u01b1\7g\2\2\u01b1\u01b2") + buf.write("\7n\2\2\u01b2\u01b3\7c\2\2\u01b3\u01b4\7r\2\2\u01b4\u01b5") + buf.write("\7u\2\2\u01b5\u01b6\7g\2\2\u01b6\u01b7\7f\2\2\u01b7Z\3") + buf.write("\2\2\2\u01b8\u01b9\7g\2\2\u01b9\u01ba\7x\2\2\u01ba\u01bb") + buf.write("\7g\2\2\u01bb\u01bc\7t\2\2\u01bc\u01bd\7{\2\2\u01bd\\") + buf.write("\3\2\2\2\u01be\u01bf\7x\2\2\u01bf\u01c0\7c\2\2\u01c0\u01c1") + buf.write("\7t\2\2\u01c1^\3\2\2\2\u01c2\u01c3\7u\2\2\u01c3\u01c4") + buf.write("\7c\2\2\u01c4\u01c5\7o\2\2\u01c5\u01c6\7r\2\2\u01c6\u01c7") + buf.write("\7n\2\2\u01c7\u01c8\7g\2\2\u01c8`\3\2\2\2\u01c9\u01ca") + buf.write("\7y\2\2\u01ca\u01cb\7k\2\2\u01cb\u01cc\7v\2\2\u01cc\u01cd") + buf.write("\7j\2\2\u01cdb\3\2\2\2\u01ce\u01cf\7m\2\2\u01cf\u01d0") + buf.write("\7g\2\2\u01d0\u01d1\7g\2\2\u01d1\u01d2\7r\2\2\u01d2d\3") + buf.write("\2\2\2\u01d3\u01d4\7f\2\2\u01d4\u01d5\7g\2\2\u01d5\u01d6") + buf.write("\7h\2\2\u01d6\u01d7\7c\2\2\u01d7\u01d8\7w\2\2\u01d8\u01d9") + buf.write("\7n\2\2\u01d9\u01da\7v\2\2\u01daf\3\2\2\2\u01db\u01dc") + buf.write("\7j\2\2\u01dc\u01dd\7c\2\2\u01dd\u01de\7t\2\2\u01de\u01df") + buf.write("\7f\2\2\u01dfh\3\2\2\2\u01e0\u01e1\7t\2\2\u01e1\u01e2") + buf.write("\7g\2\2\u01e2\u01e3\7o\2\2\u01e3\u01e4\7q\2\2\u01e4\u01e5") + buf.write("\7x\2\2\u01e5\u01e6\7g\2\2\u01e6\u01e7\7a\2\2\u01e7\u01e8") + buf.write("\7f\2\2\u01e8\u01e9\7g\2\2\u01e9\u01ea\7h\2\2\u01ea\u01eb") + buf.write("\7c\2\2\u01eb\u01ec\7w\2\2\u01ec\u01ed\7n\2\2\u01ed\u01ee") + buf.write("\7v\2\2\u01eej\3\2\2\2\u01ef\u01f0\7q\2\2\u01f0\u01f1") + buf.write("\7p\2\2\u01f1l\3\2\2\2\u01f2\u01f3\7f\2\2\u01f3\u01f4") + buf.write("\7q\2\2\u01f4n\3\2\2\2\u01f5\u01f6\7u\2\2\u01f6\u01f7") + buf.write("\7g\2\2\u01f7\u01f8\7t\2\2\u01f8\u01f9\7k\2\2\u01f9\u01fa") + buf.write("\7c\2\2\u01fa\u01fb\7n\2\2\u01fbp\3\2\2\2\u01fc\u01fd") + buf.write("\7q\2\2\u01fd\u01fe\7p\2\2\u01fe\u01ff\7g\2\2\u01ff\u0200") + buf.write("\7a\2\2\u0200\u0201\7q\2\2\u0201\u0202\7h\2\2\u0202r\3") + buf.write("\2\2\2\u0203\u0204\7r\2\2\u0204\u0205\7c\2\2\u0205\u0206") + buf.write("\7t\2\2\u0206\u0207\7c\2\2\u0207\u0208\7n\2\2\u0208\u0209") + buf.write("\7n\2\2\u0209\u020a\7g\2\2\u020a\u020b\7n\2\2\u020bt\3") + buf.write("\2\2\2\u020c\u020d\7y\2\2\u020d\u020e\7c\2\2\u020e\u020f") + buf.write("\7k\2\2\u020f\u0210\7v\2\2\u0210v\3\2\2\2\u0211\u0212") + buf.write("\7g\2\2\u0212\u0213\7o\2\2\u0213\u0214\7k\2\2\u0214\u0215") + buf.write("\7v\2\2\u0215x\3\2\2\2\u0216\u0217\7e\2\2\u0217\u0218") + buf.write("\7c\2\2\u0218\u0219\7n\2\2\u0219\u021a\7n\2\2\u021az\3") + buf.write("\2\2\2\u021b\u021c\7w\2\2\u021c\u021d\7p\2\2\u021d\u021e") + buf.write("\7v\2\2\u021e\u021f\7k\2\2\u021f\u0220\7n\2\2\u0220|\3") + buf.write("\2\2\2\u0221\u0222\7f\2\2\u0222\u0223\7g\2\2\u0223\u0224") + buf.write("\7h\2\2\u0224~\3\2\2\2\u0225\u0226\7/\2\2\u0226\u0227") + buf.write("\7@\2\2\u0227\u0080\3\2\2\2\u0228\u0229\7g\2\2\u0229\u022a") + buf.write("\7z\2\2\u022a\u022b\7r\2\2\u022b\u022c\7t\2\2\u022c\u022d") + buf.write("\7g\2\2\u022d\u022e\7u\2\2\u022e\u022f\7u\2\2\u022f\u0230") + buf.write("\7k\2\2\u0230\u0231\7q\2\2\u0231\u0232\7p\2\2\u0232\u0082") + buf.write("\3\2\2\2\u0233\u0234\7w\2\2\u0234\u0235\7p\2\2\u0235\u0236") + buf.write("\7f\2\2\u0236\u0237\7g\2\2\u0237\u0238\7h\2\2\u0238\u0239") + buf.write("\7k\2\2\u0239\u023a\7p\2\2\u023a\u023b\7g\2\2\u023b\u023c") + buf.write("\7f\2\2\u023c\u0084\3\2\2\2\u023d\u023e\7g\2\2\u023e\u023f") + buf.write("\7z\2\2\u023f\u0240\7v\2\2\u0240\u0241\7g\2\2\u0241\u0242") + buf.write("\7t\2\2\u0242\u0243\7p\2\2\u0243\u0244\7c\2\2\u0244\u0245") + buf.write("\7n\2\2\u0245\u0086\3\2\2\2\u0246\u0247\7q\2\2\u0247\u0248") + buf.write("\7p\2\2\u0248\u0249\7n\2\2\u0249\u024a\7{\2\2\u024a\u0088") + buf.write("\3\2\2\2\u024b\u024c\7e\2\2\u024c\u024d\7q\2\2\u024d\u024e") + buf.write("\7x\2\2\u024e\u024f\7g\2\2\u024f\u0250\7t\2\2\u0250\u008a") + buf.write("\3\2\2\2\u0251\u0252\7t\2\2\u0252\u0253\7g\2\2\u0253\u0254") + buf.write("\7e\2\2\u0254\u0255\7q\2\2\u0255\u0256\7t\2\2\u0256\u0257") + buf.write("\7f\2\2\u0257\u008c\3\2\2\2\u0258\u0259\7t\2\2\u0259\u025a") + buf.write("\7c\2\2\u025a\u025b\7p\2\2\u025b\u025c\7i\2\2\u025c\u025d") + buf.write("\7g\2\2\u025d\u008e\3\2\2\2\u025e\u025f\7A\2\2\u025f\u0090") + buf.write("\3\2\2\2\u0260\u0261\7?\2\2\u0261\u0262\7@\2\2\u0262\u0092") + buf.write("\3\2\2\2\u0263\u0264\7q\2\2\u0264\u0265\7t\2\2\u0265\u0094") + buf.write("\3\2\2\2\u0266\u0267\7c\2\2\u0267\u0268\7p\2\2\u0268\u0269") + buf.write("\7f\2\2\u0269\u0096\3\2\2\2\u026a\u026b\7p\2\2\u026b\u026c") + buf.write("\7q\2\2\u026c\u026d\7v\2\2\u026d\u0098\3\2\2\2\u026e\u026f") + buf.write("\7#\2\2\u026f\u0270\7?\2\2\u0270\u009a\3\2\2\2\u0271\u0272") + buf.write("\7>\2\2\u0272\u009c\3\2\2\2\u0273\u0274\7>\2\2\u0274\u0275") + buf.write("\7?\2\2\u0275\u009e\3\2\2\2\u0276\u0277\7@\2\2\u0277\u00a0") + buf.write("\3\2\2\2\u0278\u0279\7@\2\2\u0279\u027a\7?\2\2\u027a\u00a2") + buf.write("\3\2\2\2\u027b\u027c\7k\2\2\u027c\u027d\7p\2\2\u027d\u00a4") + buf.write("\3\2\2\2\u027e\u027f\7-\2\2\u027f\u00a6\3\2\2\2\u0280") + buf.write("\u0281\7/\2\2\u0281\u00a8\3\2\2\2\u0282\u0283\7,\2\2\u0283") + buf.write("\u00aa\3\2\2\2\u0284\u0285\7\61\2\2\u0285\u00ac\3\2\2") + buf.write("\2\u0286\u0287\7\'\2\2\u0287\u00ae\3\2\2\2\u0288\u0289") + buf.write("\7k\2\2\u0289\u028a\7v\2\2\u028a\u00b0\3\2\2\2\u028b\u028c") + buf.write("\7\60\2\2\u028c\u028d\7\60\2\2\u028d\u00b2\3\2\2\2\u028e") + buf.write("\u028f\6Z\2\2\u028f\u029b\5\u00bf`\2\u0290\u0292\7\17") + buf.write("\2\2\u0291\u0290\3\2\2\2\u0291\u0292\3\2\2\2\u0292\u0293") + buf.write("\3\2\2\2\u0293\u0296\7\f\2\2\u0294\u0296\4\16\17\2\u0295") + buf.write("\u0291\3\2\2\2\u0295\u0294\3\2\2\2\u0296\u0298\3\2\2\2") + buf.write("\u0297\u0299\5\u00bf`\2\u0298\u0297\3\2\2\2\u0298\u0299") + buf.write("\3\2\2\2\u0299\u029b\3\2\2\2\u029a\u028e\3\2\2\2\u029a") + buf.write("\u0295\3\2\2\2\u029b\u029c\3\2\2\2\u029c\u029d\bZ\2\2") + buf.write("\u029d\u00b4\3\2\2\2\u029e\u029f\7]\2\2\u029f\u02a0\b") + buf.write("[\3\2\u02a0\u00b6\3\2\2\2\u02a1\u02a2\7_\2\2\u02a2\u02a3") + buf.write("\b\\\4\2\u02a3\u00b8\3\2\2\2\u02a4\u02a5\7*\2\2\u02a5") + buf.write("\u02a6\b]\5\2\u02a6\u00ba\3\2\2\2\u02a7\u02a8\7+\2\2\u02a8") + buf.write("\u02a9\b^\6\2\u02a9\u00bc\3\2\2\2\u02aa\u02ad\5\u00bf") + buf.write("`\2\u02ab\u02ad\5\u00c1a\2\u02ac\u02aa\3\2\2\2\u02ac\u02ab") + buf.write("\3\2\2\2\u02ad\u02ae\3\2\2\2\u02ae\u02af\b_\7\2\u02af") + buf.write("\u00be\3\2\2\2\u02b0\u02b2\t\2\2\2\u02b1\u02b0\3\2\2\2") + buf.write("\u02b2\u02b3\3\2\2\2\u02b3\u02b1\3\2\2\2\u02b3\u02b4\3") + buf.write("\2\2\2\u02b4\u00c0\3\2\2\2\u02b5\u02b7\7^\2\2\u02b6\u02b8") + buf.write("\5\u00bf`\2\u02b7\u02b6\3\2\2\2\u02b7\u02b8\3\2\2\2\u02b8") + buf.write("\u02ba\3\2\2\2\u02b9\u02bb\7\17\2\2\u02ba\u02b9\3\2\2") + buf.write("\2\u02ba\u02bb\3\2\2\2\u02bb\u02bc\3\2\2\2\u02bc\u02bd") + buf.write("\7\f\2\2\u02bd\u00c2\3\2\2\2\u02be\u02c0\7\17\2\2\u02bf") + buf.write("\u02be\3\2\2\2\u02bf\u02c0\3\2\2\2\u02c0\u02c1\3\2\2\2") + buf.write("\u02c1\u02c2\7\f\2\2\u02c2\u00c4\3\2\2\2\u02c3\u02c4\7") + buf.write("\61\2\2\u02c4\u02c5\7,\2\2\u02c5\u02c9\3\2\2\2\u02c6\u02c8") + buf.write("\13\2\2\2\u02c7\u02c6\3\2\2\2\u02c8\u02cb\3\2\2\2\u02c9") + buf.write("\u02ca\3\2\2\2\u02c9\u02c7\3\2\2\2\u02ca\u02cc\3\2\2\2") + buf.write("\u02cb\u02c9\3\2\2\2\u02cc\u02cd\7,\2\2\u02cd\u02ce\7") + buf.write("\61\2\2\u02ce\u02cf\3\2\2\2\u02cf\u02d0\bc\7\2\u02d0\u00c6") + buf.write("\3\2\2\2\u02d1\u02d5\7%\2\2\u02d2\u02d4\n\3\2\2\u02d3") + buf.write("\u02d2\3\2\2\2\u02d4\u02d7\3\2\2\2\u02d5\u02d3\3\2\2\2") + buf.write("\u02d5\u02d6\3\2\2\2\u02d6\u02d8\3\2\2\2\u02d7\u02d5\3") + buf.write("\2\2\2\u02d8\u02d9\bd\7\2\u02d9\u00c8\3\2\2\2\u02da\u02dd") + buf.write("\5\u00cbf\2\u02db\u02dd\5\u00d1i\2\u02dc\u02da\3\2\2\2") + buf.write("\u02dc\u02db\3\2\2\2\u02dd\u00ca\3\2\2\2\u02de\u02e2\7") + buf.write("$\2\2\u02df\u02e1\5\u00cdg\2\u02e0\u02df\3\2\2\2\u02e1") + buf.write("\u02e4\3\2\2\2\u02e2\u02e0\3\2\2\2\u02e2\u02e3\3\2\2\2") + buf.write("\u02e3\u02e5\3\2\2\2\u02e4\u02e2\3\2\2\2\u02e5\u02ef\7") + buf.write("$\2\2\u02e6\u02ea\7)\2\2\u02e7\u02e9\5\u00cdg\2\u02e8") + buf.write("\u02e7\3\2\2\2\u02e9\u02ec\3\2\2\2\u02ea\u02e8\3\2\2\2") + buf.write("\u02ea\u02eb\3\2\2\2\u02eb\u02ed\3\2\2\2\u02ec\u02ea\3") + buf.write("\2\2\2\u02ed\u02ef\7)\2\2\u02ee\u02de\3\2\2\2\u02ee\u02e6") + buf.write("\3\2\2\2\u02ef\u00cc\3\2\2\2\u02f0\u02f3\5\u00cfh\2\u02f1") + buf.write("\u02f3\5\u00d7l\2\u02f2\u02f0\3\2\2\2\u02f2\u02f1\3\2") + buf.write("\2\2\u02f3\u00ce\3\2\2\2\u02f4\u02f5\n\4\2\2\u02f5\u00d0") + buf.write("\3\2\2\2\u02f6\u02f7\7$\2\2\u02f7\u02f8\7$\2\2\u02f8\u02f9") + buf.write("\7$\2\2\u02f9\u02fd\3\2\2\2\u02fa\u02fc\5\u00d3j\2\u02fb") + buf.write("\u02fa\3\2\2\2\u02fc\u02ff\3\2\2\2\u02fd\u02fb\3\2\2\2") + buf.write("\u02fd\u02fe\3\2\2\2\u02fe\u0300\3\2\2\2\u02ff\u02fd\3") + buf.write("\2\2\2\u0300\u0301\7$\2\2\u0301\u0302\7$\2\2\u0302\u0311") + buf.write("\7$\2\2\u0303\u0304\7)\2\2\u0304\u0305\7)\2\2\u0305\u0306") + buf.write("\7)\2\2\u0306\u030a\3\2\2\2\u0307\u0309\5\u00d3j\2\u0308") + buf.write("\u0307\3\2\2\2\u0309\u030c\3\2\2\2\u030a\u0308\3\2\2\2") + buf.write("\u030a\u030b\3\2\2\2\u030b\u030d\3\2\2\2\u030c\u030a\3") + buf.write("\2\2\2\u030d\u030e\7)\2\2\u030e\u030f\7)\2\2\u030f\u0311") + buf.write("\7)\2\2\u0310\u02f6\3\2\2\2\u0310\u0303\3\2\2\2\u0311") + buf.write("\u00d2\3\2\2\2\u0312\u0315\5\u00d5k\2\u0313\u0315\5\u00d7") + buf.write("l\2\u0314\u0312\3\2\2\2\u0314\u0313\3\2\2\2\u0315\u00d4") + buf.write("\3\2\2\2\u0316\u0317\n\5\2\2\u0317\u00d6\3\2\2\2\u0318") + buf.write("\u0319\7^\2\2\u0319\u031d\13\2\2\2\u031a\u031b\7^\2\2") + buf.write("\u031b\u031d\5\u00c3b\2\u031c\u0318\3\2\2\2\u031c\u031a") + buf.write("\3\2\2\2\u031d\u00d8\3\2\2\2\u031e\u0320\t\6\2\2\u031f") + buf.write("\u031e\3\2\2\2\u031f\u0320\3\2\2\2\u0320\u0324\3\2\2\2") + buf.write("\u0321\u0323\5\u00e7t\2\u0322\u0321\3\2\2\2\u0323\u0326") + buf.write("\3\2\2\2\u0324\u0322\3\2\2\2\u0324\u0325\3\2\2\2\u0325") + buf.write("\u0327\3\2\2\2\u0326\u0324\3\2\2\2\u0327\u0329\7\60\2") + buf.write("\2\u0328\u032a\5\u00e7t\2\u0329\u0328\3\2\2\2\u032a\u032b") + buf.write("\3\2\2\2\u032b\u0329\3\2\2\2\u032b\u032c\3\2\2\2\u032c") + buf.write("\u0336\3\2\2\2\u032d\u032f\t\7\2\2\u032e\u0330\t\6\2\2") + buf.write("\u032f\u032e\3\2\2\2\u032f\u0330\3\2\2\2\u0330\u0332\3") + buf.write("\2\2\2\u0331\u0333\5\u00e7t\2\u0332\u0331\3\2\2\2\u0333") + buf.write("\u0334\3\2\2\2\u0334\u0332\3\2\2\2\u0334\u0335\3\2\2\2") + buf.write("\u0335\u0337\3\2\2\2\u0336\u032d\3\2\2\2\u0336\u0337\3") + buf.write("\2\2\2\u0337\u00da\3\2\2\2\u0338\u033a\5\u00e7t\2\u0339") + buf.write("\u0338\3\2\2\2\u033a\u033b\3\2\2\2\u033b\u0339\3\2\2\2") + buf.write("\u033b\u033c\3\2\2\2\u033c\u00dc\3\2\2\2\u033d\u033e\7") + buf.write("\62\2\2\u033e\u033f\7z\2\2\u033f\u0341\3\2\2\2\u0340\u0342") + buf.write("\5\u00e9u\2\u0341\u0340\3\2\2\2\u0342\u0343\3\2\2\2\u0343") + buf.write("\u0341\3\2\2\2\u0343\u0344\3\2\2\2\u0344\u00de\3\2\2\2") + buf.write("\u0345\u0347\7/\2\2\u0346\u0348\5\u00e7t\2\u0347\u0346") + buf.write("\3\2\2\2\u0348\u0349\3\2\2\2\u0349\u0347\3\2\2\2\u0349") + buf.write("\u034a\3\2\2\2\u034a\u00e0\3\2\2\2\u034b\u034c\7v\2\2") + buf.write("\u034c\u034d\7t\2\2\u034d\u034e\7w\2\2\u034e\u0355\7g") + buf.write("\2\2\u034f\u0350\7h\2\2\u0350\u0351\7c\2\2\u0351\u0352") + buf.write("\7n\2\2\u0352\u0353\7u\2\2\u0353\u0355\7g\2\2\u0354\u034b") + buf.write("\3\2\2\2\u0354\u034f\3\2\2\2\u0355\u00e2\3\2\2\2\u0356") + buf.write("\u035a\t\b\2\2\u0357\u0359\t\t\2\2\u0358\u0357\3\2\2\2") + buf.write("\u0359\u035c\3\2\2\2\u035a\u0358\3\2\2\2\u035a\u035b\3") + buf.write("\2\2\2\u035b\u0365\3\2\2\2\u035c\u035a\3\2\2\2\u035d\u035f") + buf.write("\7~\2\2\u035e\u0360\n\n\2\2\u035f\u035e\3\2\2\2\u0360") + buf.write("\u0361\3\2\2\2\u0361\u035f\3\2\2\2\u0361\u0362\3\2\2\2") + buf.write("\u0362\u0363\3\2\2\2\u0363\u0365\7~\2\2\u0364\u0356\3") + buf.write("\2\2\2\u0364\u035d\3\2\2\2\u0365\u00e4\3\2\2\2\u0366\u0367") + buf.write("\n\n\2\2\u0367\u00e6\3\2\2\2\u0368\u0369\t\13\2\2\u0369") + buf.write("\u00e8\3\2\2\2\u036a\u036b\t\f\2\2\u036b\u00ea\3\2\2\2") + buf.write("%\2\u0291\u0295\u0298\u029a\u02ac\u02b3\u02b7\u02ba\u02bf") + buf.write("\u02c9\u02d5\u02dc\u02e2\u02ea\u02ee\u02f2\u02fd\u030a") + buf.write("\u0310\u0314\u031c\u031f\u0324\u032b\u032f\u0334\u0336") + buf.write("\u033b\u0343\u0349\u0354\u035a\u0361\u0364\b\3Z\2\3[\3") + buf.write("\3\\\4\3]\5\3^\6\b\2\2") + return buf.getvalue() + + +class OpenSCENARIO2Lexer(Lexer): + + atn = ATNDeserializer().deserialize(serializedATN()) + + decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)] + + T__0 = 1 + T__1 = 2 + T__2 = 3 + T__3 = 4 + T__4 = 5 + T__5 = 6 + T__6 = 7 + T__7 = 8 + T__8 = 9 + T__9 = 10 + T__10 = 11 + T__11 = 12 + T__12 = 13 + T__13 = 14 + T__14 = 15 + T__15 = 16 + T__16 = 17 + T__17 = 18 + T__18 = 19 + T__19 = 20 + T__20 = 21 + T__21 = 22 + T__22 = 23 + T__23 = 24 + T__24 = 25 + T__25 = 26 + T__26 = 27 + T__27 = 28 + T__28 = 29 + T__29 = 30 + T__30 = 31 + T__31 = 32 + T__32 = 33 + T__33 = 34 + T__34 = 35 + T__35 = 36 + T__36 = 37 + T__37 = 38 + T__38 = 39 + T__39 = 40 + T__40 = 41 + T__41 = 42 + T__42 = 43 + T__43 = 44 + T__44 = 45 + T__45 = 46 + T__46 = 47 + T__47 = 48 + T__48 = 49 + T__49 = 50 + T__50 = 51 + T__51 = 52 + T__52 = 53 + T__53 = 54 + T__54 = 55 + T__55 = 56 + T__56 = 57 + T__57 = 58 + T__58 = 59 + T__59 = 60 + T__60 = 61 + T__61 = 62 + T__62 = 63 + T__63 = 64 + T__64 = 65 + T__65 = 66 + T__66 = 67 + T__67 = 68 + T__68 = 69 + T__69 = 70 + T__70 = 71 + T__71 = 72 + T__72 = 73 + T__73 = 74 + T__74 = 75 + T__75 = 76 + T__76 = 77 + T__77 = 78 + T__78 = 79 + T__79 = 80 + T__80 = 81 + T__81 = 82 + T__82 = 83 + T__83 = 84 + T__84 = 85 + T__85 = 86 + T__86 = 87 + T__87 = 88 + NEWLINE = 89 + OPEN_BRACK = 90 + CLOSE_BRACK = 91 + OPEN_PAREN = 92 + CLOSE_PAREN = 93 + SKIP_ = 94 + BLOCK_COMMENT = 95 + LINE_COMMENT = 96 + StringLiteral = 97 + FloatLiteral = 98 + UintLiteral = 99 + HexUintLiteral = 100 + IntLiteral = 101 + BoolLiteral = 102 + Identifier = 103 + + channelNames = [u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN"] + + modeNames = ["DEFAULT_MODE"] + + literalNames = ["", + "'import'", "'.'", "'type'", "'is'", "'SI'", "'unit'", "'of'", + "','", "':'", "'factor'", "'offset'", "'kg'", "'m'", "'s'", + "'A'", "'K'", "'mol'", "'cd'", "'rad'", "'enum'", "'='", "'!'", + "'=='", "'struct'", "'inherits'", "'actor'", "'scenario'", "'action'", + "'modifier'", "'extend'", "'global'", "'list'", "'int'", "'uint'", + "'float'", "'bool'", "'string'", "'event'", "'if'", "'@'", "'as'", + "'rise'", "'fall'", "'elapsed'", "'every'", "'var'", "'sample'", + "'with'", "'keep'", "'default'", "'hard'", "'remove_default'", + "'on'", "'do'", "'serial'", "'one_of'", "'parallel'", "'wait'", + "'emit'", "'call'", "'until'", "'def'", "'->'", "'expression'", + "'undefined'", "'external'", "'only'", "'cover'", "'record'", + "'range'", "'?'", "'=>'", "'or'", "'and'", "'not'", "'!='", + "'<'", "'<='", "'>'", "'>='", "'in'", "'+'", "'-'", "'*'", "'/'", + "'%'", "'it'", "'..'", "'['", "']'", "'('", "')'"] + + symbolicNames = ["", + "NEWLINE", "OPEN_BRACK", "CLOSE_BRACK", "OPEN_PAREN", "CLOSE_PAREN", + "SKIP_", "BLOCK_COMMENT", "LINE_COMMENT", "StringLiteral", "FloatLiteral", + "UintLiteral", "HexUintLiteral", "IntLiteral", "BoolLiteral", + "Identifier"] + + ruleNames = ["T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", + "T__7", "T__8", "T__9", "T__10", "T__11", "T__12", "T__13", + "T__14", "T__15", "T__16", "T__17", "T__18", "T__19", + "T__20", "T__21", "T__22", "T__23", "T__24", "T__25", + "T__26", "T__27", "T__28", "T__29", "T__30", "T__31", + "T__32", "T__33", "T__34", "T__35", "T__36", "T__37", + "T__38", "T__39", "T__40", "T__41", "T__42", "T__43", + "T__44", "T__45", "T__46", "T__47", "T__48", "T__49", + "T__50", "T__51", "T__52", "T__53", "T__54", "T__55", + "T__56", "T__57", "T__58", "T__59", "T__60", "T__61", + "T__62", "T__63", "T__64", "T__65", "T__66", "T__67", + "T__68", "T__69", "T__70", "T__71", "T__72", "T__73", + "T__74", "T__75", "T__76", "T__77", "T__78", "T__79", + "T__80", "T__81", "T__82", "T__83", "T__84", "T__85", + "T__86", "T__87", "NEWLINE", "OPEN_BRACK", "CLOSE_BRACK", + "OPEN_PAREN", "CLOSE_PAREN", "SKIP_", "SPACES", "LINE_JOINING", + "RN", "BLOCK_COMMENT", "LINE_COMMENT", "StringLiteral", + "Shortstring", "ShortstringElem", "ShortstringChar", "Longstring", + "LongstringElem", "LongstringChar", "StringEscapeSeq", + "FloatLiteral", "UintLiteral", "HexUintLiteral", "IntLiteral", + "BoolLiteral", "Identifier", "NonVerticalLineChar", "Digit", + "HexDigit"] + + grammarFileName = "OpenSCENARIO2.g4" + + def __init__(self, input=None, output: TextIO = sys.stdout): + super().__init__(input, output) + self.checkVersion("4.7.2") + self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) + self._actions = None + self._predicates = None + + @property + def tokens(self): + try: + return self._tokens + except AttributeError: + self._tokens = [] + return self._tokens + + @property + def indents(self): + try: + return self._indents + except AttributeError: + self._indents = [] + return self._indents + + @property + def opened(self): + try: + return self._opened + except AttributeError: + self._opened = 0 + return self._opened + + @opened.setter + def opened(self, value): + self._opened = value + + @property + def lastToken(self): + try: + return self._lastToken + except AttributeError: + self._lastToken = None + return self._lastToken + + @lastToken.setter + def lastToken(self, value): + self._lastToken = value + + def reset(self): + super().reset() + self.tokens = [] + self.indents = [] + self.opened = 0 + self.lastToken = None + + def emitToken(self, t): + super().emitToken(t) + self.tokens.append(t) + + def nextToken(self): + if self._input.LA(1) == Token.EOF and self.indents: + for i in range(len(self.tokens)-1, -1, -1): + if self.tokens[i].type == Token.EOF: + self.tokens.pop(i) + self.emitToken(self.commonToken(LanguageParser.NEWLINE, '\n')) + while self.indents: + self.emitToken(self.createDedent()) + self.indents.pop() + self.emitToken(self.commonToken(LanguageParser.EOF, "")) + next = super().nextToken() + if next.channel == Token.DEFAULT_CHANNEL: + self.lastToken = next + return next if not self.tokens else self.tokens.pop(0) + + def createDedent(self): + dedent = self.commonToken(LanguageParser.DEDENT, "") + dedent.line = self.lastToken.line + return dedent + + def commonToken(self, type, text, indent=0): + stop = self.getCharIndex()-1-indent + start = (stop - len(text) + 1) if text else stop + return CommonToken(self._tokenFactorySourcePair, type, super().DEFAULT_TOKEN_CHANNEL, start, stop) + + @staticmethod + def getIndentationCount(spaces): + count = 0 + for ch in spaces: + if ch == '\t': + count += 8 - (count % 8) + else: + count += 1 + return count + + def atStartOfInput(self): + return Lexer.column.fget(self) == 0 and Lexer.line.fget(self) == 1 + + def action(self, localctx: RuleContext, ruleIndex: int, actionIndex: int): + if self._actions is None: + actions = dict() + actions[88] = self.NEWLINE_action + actions[89] = self.OPEN_BRACK_action + actions[90] = self.CLOSE_BRACK_action + actions[91] = self.OPEN_PAREN_action + actions[92] = self.CLOSE_PAREN_action + self._actions = actions + action = self._actions.get(ruleIndex, None) + if action is not None: + action(localctx, actionIndex) + else: + raise Exception("No registered action for:" + str(ruleIndex)) + + def NEWLINE_action(self, localctx: RuleContext, actionIndex: int): + if actionIndex == 0: + + tempt = Lexer.text.fget(self) + newLine = re.sub("[^\r\n\f]+", "", tempt) + spaces = re.sub("[\r\n\f]+", "", tempt) + la_char = "" + try: + la = self._input.LA(1) + la_char = chr(la) # Python does not compare char to ints directly + except ValueError: # End of file + pass + # Strip newlines inside open clauses except if we are near EOF. We keep NEWLINEs near EOF to + # satisfy the final newline needed by the single_put rule used by the REPL. + try: + nextnext_la = self._input.LA(2) + nextnext_la_char = chr(nextnext_la) + except ValueError: + nextnext_eof = True + else: + nextnext_eof = False + if self.opened > 0 or nextnext_eof is False and (la_char == '\r' or la_char == '\n' or la_char == '\f' or la_char == '#'): + self.skip() + else: + indent = self.getIndentationCount(spaces) + previous = self.indents[-1] if self.indents else 0 + self.emitToken(self.commonToken(self.NEWLINE, newLine, indent=indent)) # NEWLINE is actually the '\n' char + if indent == previous: + self.skip() + elif indent > previous: + self.indents.append(indent) + self.emitToken(self.commonToken(LanguageParser.INDENT, spaces)) + else: + while self.indents and self.indents[-1] > indent: + self.emitToken(self.createDedent()) + self.indents.pop() + + def OPEN_BRACK_action(self, localctx: RuleContext, actionIndex: int): + if actionIndex == 1: + self.opened += 1 + + def CLOSE_BRACK_action(self, localctx: RuleContext, actionIndex: int): + if actionIndex == 2: + self.opened -= 1 + + def OPEN_PAREN_action(self, localctx: RuleContext, actionIndex: int): + if actionIndex == 3: + self.opened += 1 + + def CLOSE_PAREN_action(self, localctx: RuleContext, actionIndex: int): + if actionIndex == 4: + self.opened -= 1 + + def sempred(self, localctx: RuleContext, ruleIndex: int, predIndex: int): + if self._predicates is None: + preds = dict() + preds[88] = self.NEWLINE_sempred + self._predicates = preds + pred = self._predicates.get(ruleIndex, None) + if pred is not None: + return pred(localctx, predIndex) + else: + raise Exception("No registered predicate for:" + str(ruleIndex)) + + def NEWLINE_sempred(self, localctx: RuleContext, predIndex: int): + if predIndex == 0: + return self.atStartOfInput() diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.tokens b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.tokens new file mode 100644 index 00000000..402155cd --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Lexer.tokens @@ -0,0 +1,195 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +T__13=14 +T__14=15 +T__15=16 +T__16=17 +T__17=18 +T__18=19 +T__19=20 +T__20=21 +T__21=22 +T__22=23 +T__23=24 +T__24=25 +T__25=26 +T__26=27 +T__27=28 +T__28=29 +T__29=30 +T__30=31 +T__31=32 +T__32=33 +T__33=34 +T__34=35 +T__35=36 +T__36=37 +T__37=38 +T__38=39 +T__39=40 +T__40=41 +T__41=42 +T__42=43 +T__43=44 +T__44=45 +T__45=46 +T__46=47 +T__47=48 +T__48=49 +T__49=50 +T__50=51 +T__51=52 +T__52=53 +T__53=54 +T__54=55 +T__55=56 +T__56=57 +T__57=58 +T__58=59 +T__59=60 +T__60=61 +T__61=62 +T__62=63 +T__63=64 +T__64=65 +T__65=66 +T__66=67 +T__67=68 +T__68=69 +T__69=70 +T__70=71 +T__71=72 +T__72=73 +T__73=74 +T__74=75 +T__75=76 +T__76=77 +T__77=78 +T__78=79 +T__79=80 +T__80=81 +T__81=82 +T__82=83 +T__83=84 +T__84=85 +T__85=86 +T__86=87 +T__87=88 +NEWLINE=89 +OPEN_BRACK=90 +CLOSE_BRACK=91 +OPEN_PAREN=92 +CLOSE_PAREN=93 +SKIP_=94 +BLOCK_COMMENT=95 +LINE_COMMENT=96 +StringLiteral=97 +FloatLiteral=98 +UintLiteral=99 +HexUintLiteral=100 +IntLiteral=101 +BoolLiteral=102 +Identifier=103 +'import'=1 +'.'=2 +'type'=3 +'is'=4 +'SI'=5 +'unit'=6 +'of'=7 +','=8 +':'=9 +'factor'=10 +'offset'=11 +'kg'=12 +'m'=13 +'s'=14 +'A'=15 +'K'=16 +'mol'=17 +'cd'=18 +'rad'=19 +'enum'=20 +'='=21 +'!'=22 +'=='=23 +'struct'=24 +'inherits'=25 +'actor'=26 +'scenario'=27 +'action'=28 +'modifier'=29 +'extend'=30 +'global'=31 +'list'=32 +'int'=33 +'uint'=34 +'float'=35 +'bool'=36 +'string'=37 +'event'=38 +'if'=39 +'@'=40 +'as'=41 +'rise'=42 +'fall'=43 +'elapsed'=44 +'every'=45 +'var'=46 +'sample'=47 +'with'=48 +'keep'=49 +'default'=50 +'hard'=51 +'remove_default'=52 +'on'=53 +'do'=54 +'serial'=55 +'one_of'=56 +'parallel'=57 +'wait'=58 +'emit'=59 +'call'=60 +'until'=61 +'def'=62 +'->'=63 +'expression'=64 +'undefined'=65 +'external'=66 +'only'=67 +'cover'=68 +'record'=69 +'range'=70 +'?'=71 +'=>'=72 +'or'=73 +'and'=74 +'not'=75 +'!='=76 +'<'=77 +'<='=78 +'>'=79 +'>='=80 +'in'=81 +'+'=82 +'-'=83 +'*'=84 +'/'=85 +'%'=86 +'it'=87 +'..'=88 +'['=90 +']'=91 +'('=92 +')'=93 diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Listener.py b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Listener.py new file mode 100644 index 00000000..30e9d44b --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Listener.py @@ -0,0 +1,1251 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# Generated from OpenSCENARIO2.g4 by ANTLR 4.7.2 +from antlr4 import * +if __name__ is not None and "." in __name__: + from .OpenSCENARIO2Parser import OpenSCENARIO2Parser +else: + from OpenSCENARIO2Parser import OpenSCENARIO2Parser + +# This class defines a complete listener for a parse tree produced by OpenSCENARIO2Parser. + + +class OpenSCENARIO2Listener(ParseTreeListener): + + # Enter a parse tree produced by OpenSCENARIO2Parser#osc_file. + def enterOsc_file(self, ctx: OpenSCENARIO2Parser.Osc_fileContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#osc_file. + def exitOsc_file(self, ctx: OpenSCENARIO2Parser.Osc_fileContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#preludeStatement. + def enterPreludeStatement(self, ctx: OpenSCENARIO2Parser.PreludeStatementContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#preludeStatement. + def exitPreludeStatement(self, ctx: OpenSCENARIO2Parser.PreludeStatementContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#importStatement. + def enterImportStatement(self, ctx: OpenSCENARIO2Parser.ImportStatementContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#importStatement. + def exitImportStatement(self, ctx: OpenSCENARIO2Parser.ImportStatementContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#importReference. + def enterImportReference(self, ctx: OpenSCENARIO2Parser.ImportReferenceContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#importReference. + def exitImportReference(self, ctx: OpenSCENARIO2Parser.ImportReferenceContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#structuredIdentifier. + def enterStructuredIdentifier(self, ctx: OpenSCENARIO2Parser.StructuredIdentifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structuredIdentifier. + def exitStructuredIdentifier(self, ctx: OpenSCENARIO2Parser.StructuredIdentifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#oscDeclaration. + def enterOscDeclaration(self, ctx: OpenSCENARIO2Parser.OscDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#oscDeclaration. + def exitOscDeclaration(self, ctx: OpenSCENARIO2Parser.OscDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#physicalTypeDeclaration. + def enterPhysicalTypeDeclaration(self, ctx: OpenSCENARIO2Parser.PhysicalTypeDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#physicalTypeDeclaration. + def exitPhysicalTypeDeclaration(self, ctx: OpenSCENARIO2Parser.PhysicalTypeDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#physicalTypeName. + def enterPhysicalTypeName(self, ctx: OpenSCENARIO2Parser.PhysicalTypeNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#physicalTypeName. + def exitPhysicalTypeName(self, ctx: OpenSCENARIO2Parser.PhysicalTypeNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#baseUnitSpecifier. + def enterBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.BaseUnitSpecifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#baseUnitSpecifier. + def exitBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.BaseUnitSpecifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#sIBaseUnitSpecifier. + def enterSIBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SIBaseUnitSpecifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#sIBaseUnitSpecifier. + def exitSIBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SIBaseUnitSpecifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#unitDeclaration. + def enterUnitDeclaration(self, ctx: OpenSCENARIO2Parser.UnitDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#unitDeclaration. + def exitUnitDeclaration(self, ctx: OpenSCENARIO2Parser.UnitDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#unitSpecifier. + def enterUnitSpecifier(self, ctx: OpenSCENARIO2Parser.UnitSpecifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#unitSpecifier. + def exitUnitSpecifier(self, ctx: OpenSCENARIO2Parser.UnitSpecifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#unitName. + def enterUnitName(self, ctx: OpenSCENARIO2Parser.UnitNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#unitName. + def exitUnitName(self, ctx: OpenSCENARIO2Parser.UnitNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#siBaseExponentList. + def enterSiBaseExponentList(self, ctx: OpenSCENARIO2Parser.SiBaseExponentListContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#siBaseExponentList. + def exitSiBaseExponentList(self, ctx: OpenSCENARIO2Parser.SiBaseExponentListContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#siBaseExponent. + def enterSiBaseExponent(self, ctx: OpenSCENARIO2Parser.SiBaseExponentContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#siBaseExponent. + def exitSiBaseExponent(self, ctx: OpenSCENARIO2Parser.SiBaseExponentContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#siUnitSpecifier. + def enterSiUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SiUnitSpecifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#siUnitSpecifier. + def exitSiUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SiUnitSpecifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#siFactor. + def enterSiFactor(self, ctx: OpenSCENARIO2Parser.SiFactorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#siFactor. + def exitSiFactor(self, ctx: OpenSCENARIO2Parser.SiFactorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#siOffset. + def enterSiOffset(self, ctx: OpenSCENARIO2Parser.SiOffsetContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#siOffset. + def exitSiOffset(self, ctx: OpenSCENARIO2Parser.SiOffsetContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#siBaseUnitName. + def enterSiBaseUnitName(self, ctx: OpenSCENARIO2Parser.SiBaseUnitNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#siBaseUnitName. + def exitSiBaseUnitName(self, ctx: OpenSCENARIO2Parser.SiBaseUnitNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumDeclaration. + def enterEnumDeclaration(self, ctx: OpenSCENARIO2Parser.EnumDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumDeclaration. + def exitEnumDeclaration(self, ctx: OpenSCENARIO2Parser.EnumDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumMemberDecl. + def enterEnumMemberDecl(self, ctx: OpenSCENARIO2Parser.EnumMemberDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumMemberDecl. + def exitEnumMemberDecl(self, ctx: OpenSCENARIO2Parser.EnumMemberDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumMemberValue. + def enterEnumMemberValue(self, ctx: OpenSCENARIO2Parser.EnumMemberValueContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumMemberValue. + def exitEnumMemberValue(self, ctx: OpenSCENARIO2Parser.EnumMemberValueContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumName. + def enterEnumName(self, ctx: OpenSCENARIO2Parser.EnumNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumName. + def exitEnumName(self, ctx: OpenSCENARIO2Parser.EnumNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumMemberName. + def enterEnumMemberName(self, ctx: OpenSCENARIO2Parser.EnumMemberNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumMemberName. + def exitEnumMemberName(self, ctx: OpenSCENARIO2Parser.EnumMemberNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumValueReference. + def enterEnumValueReference(self, ctx: OpenSCENARIO2Parser.EnumValueReferenceContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumValueReference. + def exitEnumValueReference(self, ctx: OpenSCENARIO2Parser.EnumValueReferenceContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#inheritsCondition. + def enterInheritsCondition(self, ctx: OpenSCENARIO2Parser.InheritsConditionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#inheritsCondition. + def exitInheritsCondition(self, ctx: OpenSCENARIO2Parser.InheritsConditionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#structDeclaration. + def enterStructDeclaration(self, ctx: OpenSCENARIO2Parser.StructDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structDeclaration. + def exitStructDeclaration(self, ctx: OpenSCENARIO2Parser.StructDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#structInherits. + def enterStructInherits(self, ctx: OpenSCENARIO2Parser.StructInheritsContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structInherits. + def exitStructInherits(self, ctx: OpenSCENARIO2Parser.StructInheritsContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#structMemberDecl. + def enterStructMemberDecl(self, ctx: OpenSCENARIO2Parser.StructMemberDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structMemberDecl. + def exitStructMemberDecl(self, ctx: OpenSCENARIO2Parser.StructMemberDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#fieldName. + def enterFieldName(self, ctx: OpenSCENARIO2Parser.FieldNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#fieldName. + def exitFieldName(self, ctx: OpenSCENARIO2Parser.FieldNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#structName. + def enterStructName(self, ctx: OpenSCENARIO2Parser.StructNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structName. + def exitStructName(self, ctx: OpenSCENARIO2Parser.StructNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorDeclaration. + def enterActorDeclaration(self, ctx: OpenSCENARIO2Parser.ActorDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorDeclaration. + def exitActorDeclaration(self, ctx: OpenSCENARIO2Parser.ActorDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorInherits. + def enterActorInherits(self, ctx: OpenSCENARIO2Parser.ActorInheritsContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorInherits. + def exitActorInherits(self, ctx: OpenSCENARIO2Parser.ActorInheritsContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorMemberDecl. + def enterActorMemberDecl(self, ctx: OpenSCENARIO2Parser.ActorMemberDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorMemberDecl. + def exitActorMemberDecl(self, ctx: OpenSCENARIO2Parser.ActorMemberDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorName. + def enterActorName(self, ctx: OpenSCENARIO2Parser.ActorNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorName. + def exitActorName(self, ctx: OpenSCENARIO2Parser.ActorNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#scenarioDeclaration. + def enterScenarioDeclaration(self, ctx: OpenSCENARIO2Parser.ScenarioDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#scenarioDeclaration. + def exitScenarioDeclaration(self, ctx: OpenSCENARIO2Parser.ScenarioDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#scenarioInherits. + def enterScenarioInherits(self, ctx: OpenSCENARIO2Parser.ScenarioInheritsContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#scenarioInherits. + def exitScenarioInherits(self, ctx: OpenSCENARIO2Parser.ScenarioInheritsContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#scenarioMemberDecl. + def enterScenarioMemberDecl(self, ctx: OpenSCENARIO2Parser.ScenarioMemberDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#scenarioMemberDecl. + def exitScenarioMemberDecl(self, ctx: OpenSCENARIO2Parser.ScenarioMemberDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#qualifiedBehaviorName. + def enterQualifiedBehaviorName(self, ctx: OpenSCENARIO2Parser.QualifiedBehaviorNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#qualifiedBehaviorName. + def exitQualifiedBehaviorName(self, ctx: OpenSCENARIO2Parser.QualifiedBehaviorNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorName. + def enterBehaviorName(self, ctx: OpenSCENARIO2Parser.BehaviorNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorName. + def exitBehaviorName(self, ctx: OpenSCENARIO2Parser.BehaviorNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actionDeclaration. + def enterActionDeclaration(self, ctx: OpenSCENARIO2Parser.ActionDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actionDeclaration. + def exitActionDeclaration(self, ctx: OpenSCENARIO2Parser.ActionDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actionInherits. + def enterActionInherits(self, ctx: OpenSCENARIO2Parser.ActionInheritsContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actionInherits. + def exitActionInherits(self, ctx: OpenSCENARIO2Parser.ActionInheritsContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#modifierDeclaration. + def enterModifierDeclaration(self, ctx: OpenSCENARIO2Parser.ModifierDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#modifierDeclaration. + def exitModifierDeclaration(self, ctx: OpenSCENARIO2Parser.ModifierDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#modifierName. + def enterModifierName(self, ctx: OpenSCENARIO2Parser.ModifierNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#modifierName. + def exitModifierName(self, ctx: OpenSCENARIO2Parser.ModifierNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#typeExtension. + def enterTypeExtension(self, ctx: OpenSCENARIO2Parser.TypeExtensionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#typeExtension. + def exitTypeExtension(self, ctx: OpenSCENARIO2Parser.TypeExtensionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#enumTypeExtension. + def enterEnumTypeExtension(self, ctx: OpenSCENARIO2Parser.EnumTypeExtensionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#enumTypeExtension. + def exitEnumTypeExtension(self, ctx: OpenSCENARIO2Parser.EnumTypeExtensionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#structuredTypeExtension. + def enterStructuredTypeExtension(self, ctx: OpenSCENARIO2Parser.StructuredTypeExtensionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#structuredTypeExtension. + def exitStructuredTypeExtension(self, ctx: OpenSCENARIO2Parser.StructuredTypeExtensionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#extendableTypeName. + def enterExtendableTypeName(self, ctx: OpenSCENARIO2Parser.ExtendableTypeNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#extendableTypeName. + def exitExtendableTypeName(self, ctx: OpenSCENARIO2Parser.ExtendableTypeNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#extensionMemberDecl. + def enterExtensionMemberDecl(self, ctx: OpenSCENARIO2Parser.ExtensionMemberDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#extensionMemberDecl. + def exitExtensionMemberDecl(self, ctx: OpenSCENARIO2Parser.ExtensionMemberDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#globalParameterDeclaration. + def enterGlobalParameterDeclaration(self, ctx: OpenSCENARIO2Parser.GlobalParameterDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#globalParameterDeclaration. + def exitGlobalParameterDeclaration(self, ctx: OpenSCENARIO2Parser.GlobalParameterDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#typeDeclarator. + def enterTypeDeclarator(self, ctx: OpenSCENARIO2Parser.TypeDeclaratorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#typeDeclarator. + def exitTypeDeclarator(self, ctx: OpenSCENARIO2Parser.TypeDeclaratorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#nonAggregateTypeDeclarator. + def enterNonAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.NonAggregateTypeDeclaratorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#nonAggregateTypeDeclarator. + def exitNonAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.NonAggregateTypeDeclaratorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#aggregateTypeDeclarator. + def enterAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.AggregateTypeDeclaratorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#aggregateTypeDeclarator. + def exitAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.AggregateTypeDeclaratorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#listTypeDeclarator. + def enterListTypeDeclarator(self, ctx: OpenSCENARIO2Parser.ListTypeDeclaratorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#listTypeDeclarator. + def exitListTypeDeclarator(self, ctx: OpenSCENARIO2Parser.ListTypeDeclaratorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#primitiveType. + def enterPrimitiveType(self, ctx: OpenSCENARIO2Parser.PrimitiveTypeContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#primitiveType. + def exitPrimitiveType(self, ctx: OpenSCENARIO2Parser.PrimitiveTypeContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#typeName. + def enterTypeName(self, ctx: OpenSCENARIO2Parser.TypeNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#typeName. + def exitTypeName(self, ctx: OpenSCENARIO2Parser.TypeNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventDeclaration. + def enterEventDeclaration(self, ctx: OpenSCENARIO2Parser.EventDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventDeclaration. + def exitEventDeclaration(self, ctx: OpenSCENARIO2Parser.EventDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventSpecification. + def enterEventSpecification(self, ctx: OpenSCENARIO2Parser.EventSpecificationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventSpecification. + def exitEventSpecification(self, ctx: OpenSCENARIO2Parser.EventSpecificationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventReference. + def enterEventReference(self, ctx: OpenSCENARIO2Parser.EventReferenceContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventReference. + def exitEventReference(self, ctx: OpenSCENARIO2Parser.EventReferenceContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventFieldDecl. + def enterEventFieldDecl(self, ctx: OpenSCENARIO2Parser.EventFieldDeclContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventFieldDecl. + def exitEventFieldDecl(self, ctx: OpenSCENARIO2Parser.EventFieldDeclContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventFieldName. + def enterEventFieldName(self, ctx: OpenSCENARIO2Parser.EventFieldNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventFieldName. + def exitEventFieldName(self, ctx: OpenSCENARIO2Parser.EventFieldNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventName. + def enterEventName(self, ctx: OpenSCENARIO2Parser.EventNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventName. + def exitEventName(self, ctx: OpenSCENARIO2Parser.EventNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventPath. + def enterEventPath(self, ctx: OpenSCENARIO2Parser.EventPathContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventPath. + def exitEventPath(self, ctx: OpenSCENARIO2Parser.EventPathContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#eventCondition. + def enterEventCondition(self, ctx: OpenSCENARIO2Parser.EventConditionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#eventCondition. + def exitEventCondition(self, ctx: OpenSCENARIO2Parser.EventConditionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#riseExpression. + def enterRiseExpression(self, ctx: OpenSCENARIO2Parser.RiseExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#riseExpression. + def exitRiseExpression(self, ctx: OpenSCENARIO2Parser.RiseExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#fallExpression. + def enterFallExpression(self, ctx: OpenSCENARIO2Parser.FallExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#fallExpression. + def exitFallExpression(self, ctx: OpenSCENARIO2Parser.FallExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#elapsedExpression. + def enterElapsedExpression(self, ctx: OpenSCENARIO2Parser.ElapsedExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#elapsedExpression. + def exitElapsedExpression(self, ctx: OpenSCENARIO2Parser.ElapsedExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#everyExpression. + def enterEveryExpression(self, ctx: OpenSCENARIO2Parser.EveryExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#everyExpression. + def exitEveryExpression(self, ctx: OpenSCENARIO2Parser.EveryExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#boolExpression. + def enterBoolExpression(self, ctx: OpenSCENARIO2Parser.BoolExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#boolExpression. + def exitBoolExpression(self, ctx: OpenSCENARIO2Parser.BoolExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#durationExpression. + def enterDurationExpression(self, ctx: OpenSCENARIO2Parser.DurationExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#durationExpression. + def exitDurationExpression(self, ctx: OpenSCENARIO2Parser.DurationExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#fieldDeclaration. + def enterFieldDeclaration(self, ctx: OpenSCENARIO2Parser.FieldDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#fieldDeclaration. + def exitFieldDeclaration(self, ctx: OpenSCENARIO2Parser.FieldDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#parameterDeclaration. + def enterParameterDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#parameterDeclaration. + def exitParameterDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#variableDeclaration. + def enterVariableDeclaration(self, ctx: OpenSCENARIO2Parser.VariableDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#variableDeclaration. + def exitVariableDeclaration(self, ctx: OpenSCENARIO2Parser.VariableDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#sampleExpression. + def enterSampleExpression(self, ctx: OpenSCENARIO2Parser.SampleExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#sampleExpression. + def exitSampleExpression(self, ctx: OpenSCENARIO2Parser.SampleExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#defaultValue. + def enterDefaultValue(self, ctx: OpenSCENARIO2Parser.DefaultValueContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#defaultValue. + def exitDefaultValue(self, ctx: OpenSCENARIO2Parser.DefaultValueContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#parameterWithDeclaration. + def enterParameterWithDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterWithDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#parameterWithDeclaration. + def exitParameterWithDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterWithDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#parameterWithMember. + def enterParameterWithMember(self, ctx: OpenSCENARIO2Parser.ParameterWithMemberContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#parameterWithMember. + def exitParameterWithMember(self, ctx: OpenSCENARIO2Parser.ParameterWithMemberContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#constraintDeclaration. + def enterConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.ConstraintDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#constraintDeclaration. + def exitConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.ConstraintDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#keepConstraintDeclaration. + def enterKeepConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.KeepConstraintDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#keepConstraintDeclaration. + def exitKeepConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.KeepConstraintDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#constraintQualifier. + def enterConstraintQualifier(self, ctx: OpenSCENARIO2Parser.ConstraintQualifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#constraintQualifier. + def exitConstraintQualifier(self, ctx: OpenSCENARIO2Parser.ConstraintQualifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#constraintExpression. + def enterConstraintExpression(self, ctx: OpenSCENARIO2Parser.ConstraintExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#constraintExpression. + def exitConstraintExpression(self, ctx: OpenSCENARIO2Parser.ConstraintExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#removeDefaultDeclaration. + def enterRemoveDefaultDeclaration(self, ctx: OpenSCENARIO2Parser.RemoveDefaultDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#removeDefaultDeclaration. + def exitRemoveDefaultDeclaration(self, ctx: OpenSCENARIO2Parser.RemoveDefaultDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#parameterReference. + def enterParameterReference(self, ctx: OpenSCENARIO2Parser.ParameterReferenceContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#parameterReference. + def exitParameterReference(self, ctx: OpenSCENARIO2Parser.ParameterReferenceContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#modifierInvocation. + def enterModifierInvocation(self, ctx: OpenSCENARIO2Parser.ModifierInvocationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#modifierInvocation. + def exitModifierInvocation(self, ctx: OpenSCENARIO2Parser.ModifierInvocationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorExpression. + def enterBehaviorExpression(self, ctx: OpenSCENARIO2Parser.BehaviorExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorExpression. + def exitBehaviorExpression(self, ctx: OpenSCENARIO2Parser.BehaviorExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorSpecification. + def enterBehaviorSpecification(self, ctx: OpenSCENARIO2Parser.BehaviorSpecificationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorSpecification. + def exitBehaviorSpecification(self, ctx: OpenSCENARIO2Parser.BehaviorSpecificationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#onDirective. + def enterOnDirective(self, ctx: OpenSCENARIO2Parser.OnDirectiveContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#onDirective. + def exitOnDirective(self, ctx: OpenSCENARIO2Parser.OnDirectiveContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#onMember. + def enterOnMember(self, ctx: OpenSCENARIO2Parser.OnMemberContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#onMember. + def exitOnMember(self, ctx: OpenSCENARIO2Parser.OnMemberContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#doDirective. + def enterDoDirective(self, ctx: OpenSCENARIO2Parser.DoDirectiveContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#doDirective. + def exitDoDirective(self, ctx: OpenSCENARIO2Parser.DoDirectiveContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#doMember. + def enterDoMember(self, ctx: OpenSCENARIO2Parser.DoMemberContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#doMember. + def exitDoMember(self, ctx: OpenSCENARIO2Parser.DoMemberContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#composition. + def enterComposition(self, ctx: OpenSCENARIO2Parser.CompositionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#composition. + def exitComposition(self, ctx: OpenSCENARIO2Parser.CompositionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#compositionOperator. + def enterCompositionOperator(self, ctx: OpenSCENARIO2Parser.CompositionOperatorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#compositionOperator. + def exitCompositionOperator(self, ctx: OpenSCENARIO2Parser.CompositionOperatorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorInvocation. + def enterBehaviorInvocation(self, ctx: OpenSCENARIO2Parser.BehaviorInvocationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorInvocation. + def exitBehaviorInvocation(self, ctx: OpenSCENARIO2Parser.BehaviorInvocationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorWithDeclaration. + def enterBehaviorWithDeclaration(self, ctx: OpenSCENARIO2Parser.BehaviorWithDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorWithDeclaration. + def exitBehaviorWithDeclaration(self, ctx: OpenSCENARIO2Parser.BehaviorWithDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#behaviorWithMember. + def enterBehaviorWithMember(self, ctx: OpenSCENARIO2Parser.BehaviorWithMemberContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#behaviorWithMember. + def exitBehaviorWithMember(self, ctx: OpenSCENARIO2Parser.BehaviorWithMemberContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#labelName. + def enterLabelName(self, ctx: OpenSCENARIO2Parser.LabelNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#labelName. + def exitLabelName(self, ctx: OpenSCENARIO2Parser.LabelNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#actorExpression. + def enterActorExpression(self, ctx: OpenSCENARIO2Parser.ActorExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#actorExpression. + def exitActorExpression(self, ctx: OpenSCENARIO2Parser.ActorExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#waitDirective. + def enterWaitDirective(self, ctx: OpenSCENARIO2Parser.WaitDirectiveContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#waitDirective. + def exitWaitDirective(self, ctx: OpenSCENARIO2Parser.WaitDirectiveContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#emitDirective. + def enterEmitDirective(self, ctx: OpenSCENARIO2Parser.EmitDirectiveContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#emitDirective. + def exitEmitDirective(self, ctx: OpenSCENARIO2Parser.EmitDirectiveContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#callDirective. + def enterCallDirective(self, ctx: OpenSCENARIO2Parser.CallDirectiveContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#callDirective. + def exitCallDirective(self, ctx: OpenSCENARIO2Parser.CallDirectiveContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#untilDirective. + def enterUntilDirective(self, ctx: OpenSCENARIO2Parser.UntilDirectiveContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#untilDirective. + def exitUntilDirective(self, ctx: OpenSCENARIO2Parser.UntilDirectiveContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodInvocation. + def enterMethodInvocation(self, ctx: OpenSCENARIO2Parser.MethodInvocationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodInvocation. + def exitMethodInvocation(self, ctx: OpenSCENARIO2Parser.MethodInvocationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodDeclaration. + def enterMethodDeclaration(self, ctx: OpenSCENARIO2Parser.MethodDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodDeclaration. + def exitMethodDeclaration(self, ctx: OpenSCENARIO2Parser.MethodDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#returnType. + def enterReturnType(self, ctx: OpenSCENARIO2Parser.ReturnTypeContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#returnType. + def exitReturnType(self, ctx: OpenSCENARIO2Parser.ReturnTypeContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodImplementation. + def enterMethodImplementation(self, ctx: OpenSCENARIO2Parser.MethodImplementationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodImplementation. + def exitMethodImplementation(self, ctx: OpenSCENARIO2Parser.MethodImplementationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodQualifier. + def enterMethodQualifier(self, ctx: OpenSCENARIO2Parser.MethodQualifierContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodQualifier. + def exitMethodQualifier(self, ctx: OpenSCENARIO2Parser.MethodQualifierContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#methodName. + def enterMethodName(self, ctx: OpenSCENARIO2Parser.MethodNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#methodName. + def exitMethodName(self, ctx: OpenSCENARIO2Parser.MethodNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageDeclaration. + def enterCoverageDeclaration(self, ctx: OpenSCENARIO2Parser.CoverageDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageDeclaration. + def exitCoverageDeclaration(self, ctx: OpenSCENARIO2Parser.CoverageDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverDeclaration. + def enterCoverDeclaration(self, ctx: OpenSCENARIO2Parser.CoverDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverDeclaration. + def exitCoverDeclaration(self, ctx: OpenSCENARIO2Parser.CoverDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#recordDeclaration. + def enterRecordDeclaration(self, ctx: OpenSCENARIO2Parser.RecordDeclarationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#recordDeclaration. + def exitRecordDeclaration(self, ctx: OpenSCENARIO2Parser.RecordDeclarationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageExpression. + def enterCoverageExpression(self, ctx: OpenSCENARIO2Parser.CoverageExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageExpression. + def exitCoverageExpression(self, ctx: OpenSCENARIO2Parser.CoverageExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageUnit. + def enterCoverageUnit(self, ctx: OpenSCENARIO2Parser.CoverageUnitContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageUnit. + def exitCoverageUnit(self, ctx: OpenSCENARIO2Parser.CoverageUnitContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageRange. + def enterCoverageRange(self, ctx: OpenSCENARIO2Parser.CoverageRangeContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageRange. + def exitCoverageRange(self, ctx: OpenSCENARIO2Parser.CoverageRangeContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageEvery. + def enterCoverageEvery(self, ctx: OpenSCENARIO2Parser.CoverageEveryContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageEvery. + def exitCoverageEvery(self, ctx: OpenSCENARIO2Parser.CoverageEveryContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageEvent. + def enterCoverageEvent(self, ctx: OpenSCENARIO2Parser.CoverageEventContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageEvent. + def exitCoverageEvent(self, ctx: OpenSCENARIO2Parser.CoverageEventContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#coverageNameArgument. + def enterCoverageNameArgument(self, ctx: OpenSCENARIO2Parser.CoverageNameArgumentContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#coverageNameArgument. + def exitCoverageNameArgument(self, ctx: OpenSCENARIO2Parser.CoverageNameArgumentContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#targetName. + def enterTargetName(self, ctx: OpenSCENARIO2Parser.TargetNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#targetName. + def exitTargetName(self, ctx: OpenSCENARIO2Parser.TargetNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#expression. + def enterExpression(self, ctx: OpenSCENARIO2Parser.ExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#expression. + def exitExpression(self, ctx: OpenSCENARIO2Parser.ExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#ternaryOpExp. + def enterTernaryOpExp(self, ctx: OpenSCENARIO2Parser.TernaryOpExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#ternaryOpExp. + def exitTernaryOpExp(self, ctx: OpenSCENARIO2Parser.TernaryOpExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#implication. + def enterImplication(self, ctx: OpenSCENARIO2Parser.ImplicationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#implication. + def exitImplication(self, ctx: OpenSCENARIO2Parser.ImplicationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#disjunction. + def enterDisjunction(self, ctx: OpenSCENARIO2Parser.DisjunctionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#disjunction. + def exitDisjunction(self, ctx: OpenSCENARIO2Parser.DisjunctionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#conjunction. + def enterConjunction(self, ctx: OpenSCENARIO2Parser.ConjunctionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#conjunction. + def exitConjunction(self, ctx: OpenSCENARIO2Parser.ConjunctionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#inversion. + def enterInversion(self, ctx: OpenSCENARIO2Parser.InversionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#inversion. + def exitInversion(self, ctx: OpenSCENARIO2Parser.InversionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#relationExp. + def enterRelationExp(self, ctx: OpenSCENARIO2Parser.RelationExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#relationExp. + def exitRelationExp(self, ctx: OpenSCENARIO2Parser.RelationExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#sumExp. + def enterSumExp(self, ctx: OpenSCENARIO2Parser.SumExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#sumExp. + def exitSumExp(self, ctx: OpenSCENARIO2Parser.SumExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#relationalOp. + def enterRelationalOp(self, ctx: OpenSCENARIO2Parser.RelationalOpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#relationalOp. + def exitRelationalOp(self, ctx: OpenSCENARIO2Parser.RelationalOpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#termExp. + def enterTermExp(self, ctx: OpenSCENARIO2Parser.TermExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#termExp. + def exitTermExp(self, ctx: OpenSCENARIO2Parser.TermExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#additiveExp. + def enterAdditiveExp(self, ctx: OpenSCENARIO2Parser.AdditiveExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#additiveExp. + def exitAdditiveExp(self, ctx: OpenSCENARIO2Parser.AdditiveExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#additiveOp. + def enterAdditiveOp(self, ctx: OpenSCENARIO2Parser.AdditiveOpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#additiveOp. + def exitAdditiveOp(self, ctx: OpenSCENARIO2Parser.AdditiveOpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#multiplicativeExp. + def enterMultiplicativeExp(self, ctx: OpenSCENARIO2Parser.MultiplicativeExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#multiplicativeExp. + def exitMultiplicativeExp(self, ctx: OpenSCENARIO2Parser.MultiplicativeExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#factorExp. + def enterFactorExp(self, ctx: OpenSCENARIO2Parser.FactorExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#factorExp. + def exitFactorExp(self, ctx: OpenSCENARIO2Parser.FactorExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#multiplicativeOp. + def enterMultiplicativeOp(self, ctx: OpenSCENARIO2Parser.MultiplicativeOpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#multiplicativeOp. + def exitMultiplicativeOp(self, ctx: OpenSCENARIO2Parser.MultiplicativeOpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#factor. + def enterFactor(self, ctx: OpenSCENARIO2Parser.FactorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#factor. + def exitFactor(self, ctx: OpenSCENARIO2Parser.FactorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#primaryExpression. + def enterPrimaryExpression(self, ctx: OpenSCENARIO2Parser.PrimaryExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#primaryExpression. + def exitPrimaryExpression(self, ctx: OpenSCENARIO2Parser.PrimaryExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#castExpression. + def enterCastExpression(self, ctx: OpenSCENARIO2Parser.CastExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#castExpression. + def exitCastExpression(self, ctx: OpenSCENARIO2Parser.CastExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#functionApplicationExpression. + def enterFunctionApplicationExpression(self, ctx: OpenSCENARIO2Parser.FunctionApplicationExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#functionApplicationExpression. + def exitFunctionApplicationExpression(self, ctx: OpenSCENARIO2Parser.FunctionApplicationExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#fieldAccessExpression. + def enterFieldAccessExpression(self, ctx: OpenSCENARIO2Parser.FieldAccessExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#fieldAccessExpression. + def exitFieldAccessExpression(self, ctx: OpenSCENARIO2Parser.FieldAccessExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#elementAccessExpression. + def enterElementAccessExpression(self, ctx: OpenSCENARIO2Parser.ElementAccessExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#elementAccessExpression. + def exitElementAccessExpression(self, ctx: OpenSCENARIO2Parser.ElementAccessExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#typeTestExpression. + def enterTypeTestExpression(self, ctx: OpenSCENARIO2Parser.TypeTestExpressionContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#typeTestExpression. + def exitTypeTestExpression(self, ctx: OpenSCENARIO2Parser.TypeTestExpressionContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#fieldAccess. + def enterFieldAccess(self, ctx: OpenSCENARIO2Parser.FieldAccessContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#fieldAccess. + def exitFieldAccess(self, ctx: OpenSCENARIO2Parser.FieldAccessContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#primaryExp. + def enterPrimaryExp(self, ctx: OpenSCENARIO2Parser.PrimaryExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#primaryExp. + def exitPrimaryExp(self, ctx: OpenSCENARIO2Parser.PrimaryExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#valueExp. + def enterValueExp(self, ctx: OpenSCENARIO2Parser.ValueExpContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#valueExp. + def exitValueExp(self, ctx: OpenSCENARIO2Parser.ValueExpContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#listConstructor. + def enterListConstructor(self, ctx: OpenSCENARIO2Parser.ListConstructorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#listConstructor. + def exitListConstructor(self, ctx: OpenSCENARIO2Parser.ListConstructorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#rangeConstructor. + def enterRangeConstructor(self, ctx: OpenSCENARIO2Parser.RangeConstructorContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#rangeConstructor. + def exitRangeConstructor(self, ctx: OpenSCENARIO2Parser.RangeConstructorContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#identifierReference. + def enterIdentifierReference(self, ctx: OpenSCENARIO2Parser.IdentifierReferenceContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#identifierReference. + def exitIdentifierReference(self, ctx: OpenSCENARIO2Parser.IdentifierReferenceContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#argumentListSpecification. + def enterArgumentListSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentListSpecificationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#argumentListSpecification. + def exitArgumentListSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentListSpecificationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#argumentSpecification. + def enterArgumentSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentSpecificationContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#argumentSpecification. + def exitArgumentSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentSpecificationContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#argumentName. + def enterArgumentName(self, ctx: OpenSCENARIO2Parser.ArgumentNameContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#argumentName. + def exitArgumentName(self, ctx: OpenSCENARIO2Parser.ArgumentNameContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#argumentList. + def enterArgumentList(self, ctx: OpenSCENARIO2Parser.ArgumentListContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#argumentList. + def exitArgumentList(self, ctx: OpenSCENARIO2Parser.ArgumentListContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#positionalArgument. + def enterPositionalArgument(self, ctx: OpenSCENARIO2Parser.PositionalArgumentContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#positionalArgument. + def exitPositionalArgument(self, ctx: OpenSCENARIO2Parser.PositionalArgumentContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#namedArgument. + def enterNamedArgument(self, ctx: OpenSCENARIO2Parser.NamedArgumentContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#namedArgument. + def exitNamedArgument(self, ctx: OpenSCENARIO2Parser.NamedArgumentContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#physicalLiteral. + def enterPhysicalLiteral(self, ctx: OpenSCENARIO2Parser.PhysicalLiteralContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#physicalLiteral. + def exitPhysicalLiteral(self, ctx: OpenSCENARIO2Parser.PhysicalLiteralContext): + pass + + # Enter a parse tree produced by OpenSCENARIO2Parser#integerLiteral. + def enterIntegerLiteral(self, ctx: OpenSCENARIO2Parser.IntegerLiteralContext): + pass + + # Exit a parse tree produced by OpenSCENARIO2Parser#integerLiteral. + def exitIntegerLiteral(self, ctx: OpenSCENARIO2Parser.IntegerLiteralContext): + pass diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Parser.py b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Parser.py new file mode 100644 index 00000000..cbc0ce45 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Parser.py @@ -0,0 +1,10461 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# Generated from OpenSCENARIO2.g4 by ANTLR 4.7.2 +# encoding: utf-8 +from antlr4 import * +from io import StringIO +from typing.io import TextIO +import sys + + +def serializedATN(): + with StringIO() as buf: + buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\3k") + buf.write("\u0531\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7") + buf.write("\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r\4\16") + buf.write("\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23\t\23") + buf.write("\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31") + buf.write("\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36") + buf.write("\4\37\t\37\4 \t \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t") + buf.write("&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t,\4-\t-\4.\t.\4") + buf.write("/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t\64") + buf.write("\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t") + buf.write(";\4<\t<\4=\t=\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\t") + buf.write("D\4E\tE\4F\tF\4G\tG\4H\tH\4I\tI\4J\tJ\4K\tK\4L\tL\4M\t") + buf.write("M\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT\4U\tU\4V\t") + buf.write("V\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4") + buf.write("_\t_\4`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4") + buf.write("h\th\4i\ti\4j\tj\4k\tk\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4") + buf.write("q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv\4w\tw\4x\tx\4y\ty\4") + buf.write("z\tz\4{\t{\4|\t|\4}\t}\4~\t~\4\177\t\177\4\u0080\t\u0080") + buf.write("\4\u0081\t\u0081\4\u0082\t\u0082\4\u0083\t\u0083\4\u0084") + buf.write("\t\u0084\4\u0085\t\u0085\4\u0086\t\u0086\4\u0087\t\u0087") + buf.write("\4\u0088\t\u0088\4\u0089\t\u0089\4\u008a\t\u008a\4\u008b") + buf.write("\t\u008b\4\u008c\t\u008c\4\u008d\t\u008d\3\2\7\2\u011c") + buf.write("\n\2\f\2\16\2\u011f\13\2\3\2\7\2\u0122\n\2\f\2\16\2\u0125") + buf.write("\13\2\3\2\3\2\3\3\3\3\3\4\3\4\3\4\3\4\3\4\5\4\u0130\n") + buf.write("\4\3\5\3\5\5\5\u0134\n\5\3\6\3\6\3\6\3\6\3\6\3\6\7\6\u013c") + buf.write("\n\6\f\6\16\6\u013f\13\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3") + buf.write("\7\3\7\3\7\3\7\5\7\u014c\n\7\3\b\3\b\3\b\3\b\3\b\3\b\3") + buf.write("\t\3\t\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3") + buf.write("\f\3\f\3\f\3\f\3\f\3\r\3\r\3\16\3\16\5\16\u0169\n\16\3") + buf.write("\17\3\17\3\17\7\17\u016e\n\17\f\17\16\17\u0171\13\17\3") + buf.write("\20\3\20\3\20\3\20\3\21\3\21\3\21\3\21\3\21\5\21\u017c") + buf.write("\n\21\3\21\3\21\5\21\u0180\n\21\3\21\3\21\3\22\3\22\3") + buf.write("\22\3\22\5\22\u0188\n\22\3\23\3\23\3\23\3\23\5\23\u018e") + buf.write("\n\23\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25\3\25\7\25") + buf.write("\u0199\n\25\f\25\16\25\u019c\13\25\3\25\3\25\3\25\3\26") + buf.write("\3\26\3\26\5\26\u01a4\n\26\3\27\3\27\3\30\3\30\3\31\3") + buf.write("\31\3\32\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\5\33") + buf.write("\u01b5\n\33\3\33\3\33\3\34\3\34\3\34\5\34\u01bc\n\34\3") + buf.write("\34\3\34\3\34\3\34\6\34\u01c2\n\34\r\34\16\34\u01c3\3") + buf.write("\34\3\34\3\34\5\34\u01c9\n\34\3\35\3\35\3\35\5\35\u01ce") + buf.write("\n\35\3\36\3\36\3\36\3\36\3\36\5\36\u01d5\n\36\3\37\3") + buf.write("\37\3 \3 \3!\3!\3!\5!\u01de\n!\3!\3!\3!\3!\6!\u01e4\n") + buf.write("!\r!\16!\u01e5\3!\3!\3!\5!\u01eb\n!\3\"\3\"\3\"\5\"\u01f0") + buf.write("\n\"\3#\3#\3#\3#\3#\5#\u01f7\n#\3$\3$\3%\3%\3%\5%\u01fe") + buf.write("\n%\3%\3%\3%\3%\3%\6%\u0205\n%\r%\16%\u0206\3%\3%\3%\5") + buf.write("%\u020c\n%\3&\3&\3&\5&\u0211\n&\3\'\3\'\3\'\3\'\3\'\3") + buf.write("\'\5\'\u0219\n\'\3(\3(\3(\5(\u021e\n(\3(\3(\3)\3)\3*\3") + buf.write("*\3*\5*\u0227\n*\3*\3*\3*\3*\3*\6*\u022e\n*\r*\16*\u022f") + buf.write("\3*\3*\3*\5*\u0235\n*\3+\3+\3+\5+\u023a\n+\3,\3,\3,\3") + buf.write(",\5,\u0240\n,\3,\3,\3,\5,\u0245\n,\3,\3,\3,\3,\6,\u024b") + buf.write("\n,\r,\16,\u024c\3,\3,\3,\5,\u0252\n,\3-\3-\3.\3.\5.\u0258") + buf.write("\n.\3/\3/\3/\3/\3/\3/\3/\7/\u0261\n/\f/\16/\u0264\13/") + buf.write("\3/\3/\3/\3\60\3\60\3\60\3\60\3\60\3\60\6\60\u026f\n\60") + buf.write("\r\60\16\60\u0270\3\60\3\60\3\61\3\61\5\61\u0277\n\61") + buf.write("\3\62\3\62\3\62\3\62\5\62\u027d\n\62\3\63\3\63\3\63\3") + buf.write("\63\7\63\u0283\n\63\f\63\16\63\u0286\13\63\3\63\3\63\3") + buf.write("\63\3\63\5\63\u028c\n\63\3\63\3\63\5\63\u0290\n\63\3\64") + buf.write("\3\64\5\64\u0294\n\64\3\65\3\65\3\65\5\65\u0299\n\65\3") + buf.write("\66\3\66\3\67\3\67\3\67\3\67\38\38\39\39\3:\3:\3:\3:\3") + buf.write(":\3:\5:\u02ab\n:\3:\3:\5:\u02af\n:\3:\3:\3;\3;\5;\u02b5") + buf.write("\n;\3;\3;\5;\u02b9\n;\3;\5;\u02bc\n;\3<\3<\3<\3=\3=\3") + buf.write("=\3>\3>\3?\3?\3@\3@\3@\5@\u02cb\n@\3@\3@\3A\3A\3A\3A\3") + buf.write("A\5A\u02d4\nA\3B\3B\3B\3B\3B\3C\3C\3C\3C\3C\3D\3D\3D\3") + buf.write("D\3D\3E\3E\3E\3E\3E\3E\3E\3E\5E\u02ed\nE\3E\3E\3F\3F\3") + buf.write("G\3G\3H\3H\5H\u02f7\nH\3I\3I\3I\7I\u02fc\nI\fI\16I\u02ff") + buf.write("\13I\3I\3I\3I\3I\5I\u0305\nI\3I\3I\5I\u0309\nI\3J\3J\3") + buf.write("J\3J\7J\u030f\nJ\fJ\16J\u0312\13J\3J\3J\3J\3J\3J\5J\u0319") + buf.write("\nJ\5J\u031b\nJ\3J\3J\3K\3K\3K\3K\3K\3K\3K\5K\u0326\n") + buf.write("K\3K\3K\3L\3L\3M\3M\3M\3M\3M\6M\u0331\nM\rM\16M\u0332") + buf.write("\3M\3M\3N\3N\5N\u0339\nN\3O\3O\5O\u033d\nO\3P\3P\3P\5") + buf.write("P\u0342\nP\3P\3P\3P\3P\3Q\3Q\3R\3R\3S\3S\3S\3S\3S\3S\3") + buf.write("T\3T\5T\u0354\nT\3U\3U\5U\u0358\nU\3U\3U\5U\u035c\nU\3") + buf.write("U\3U\3U\5U\u0361\nU\3U\3U\3U\3V\3V\3V\3V\3V\3W\3W\5W\u036d") + buf.write("\nW\3X\3X\3X\3X\3X\3X\6X\u0375\nX\rX\16X\u0376\3X\3X\3") + buf.write("Y\3Y\5Y\u037d\nY\3Z\3Z\3Z\3[\3[\3[\5[\u0385\n[\3[\3[\3") + buf.write("[\3[\3[\5[\u038c\n[\3\\\3\\\3\\\5\\\u0391\n\\\3\\\5\\") + buf.write("\u0394\n\\\3\\\3\\\3\\\3\\\6\\\u039a\n\\\r\\\16\\\u039b") + buf.write("\3\\\3\\\5\\\u03a0\n\\\3]\3]\3^\3^\3^\5^\u03a7\n^\3^\3") + buf.write("^\3^\5^\u03ac\n^\3^\3^\3^\5^\u03b1\n^\3_\3_\3_\3_\3_\6") + buf.write("_\u03b8\n_\r_\16_\u03b9\3_\3_\3`\3`\3`\5`\u03c1\n`\3a") + buf.write("\3a\3b\3b\3c\3c\3c\3c\3d\3d\3d\3d\3d\3d\5d\u03d1\nd\3") + buf.write("d\3d\3e\3e\3e\3e\3f\3f\3f\3f\3g\3g\3g\5g\u03e0\ng\3g\3") + buf.write("g\3h\3h\3h\3h\5h\u03e8\nh\3h\3h\3h\5h\u03ed\nh\3h\3h\3") + buf.write("h\3i\3i\3j\3j\5j\u03f6\nj\3j\3j\3j\3j\3j\3j\3j\5j\u03ff") + buf.write("\nj\3j\3j\5j\u0403\nj\3k\3k\3l\3l\3m\3m\5m\u040b\nm\3") + buf.write("n\3n\3n\5n\u0410\nn\3n\7n\u0413\nn\fn\16n\u0416\13n\3") + buf.write("n\3n\3n\3o\3o\3o\5o\u041e\no\3o\7o\u0421\no\fo\16o\u0424") + buf.write("\13o\3o\3o\3o\3p\3p\3p\3p\3p\3p\3p\3p\3p\3p\3p\3p\3p\3") + buf.write("p\3p\3p\3p\3p\3p\3p\3p\3p\5p\u043f\np\3q\3q\3r\3r\5r\u0445") + buf.write("\nr\3s\3s\3s\3s\3s\3s\3t\3t\3t\7t\u0450\nt\ft\16t\u0453") + buf.write("\13t\3u\3u\3u\7u\u0458\nu\fu\16u\u045b\13u\3v\3v\3v\7") + buf.write("v\u0460\nv\fv\16v\u0463\13v\3w\3w\3w\5w\u0468\nw\3x\3") + buf.write("x\3x\3x\3x\3x\3x\7x\u0471\nx\fx\16x\u0474\13x\3y\3y\3") + buf.write("z\3z\3z\3z\3z\3z\3z\7z\u047f\nz\fz\16z\u0482\13z\3{\3") + buf.write("{\3|\3|\3|\3|\3|\3|\3|\7|\u048d\n|\f|\16|\u0490\13|\3") + buf.write("}\3}\3~\3~\3~\5~\u0497\n~\3\177\3\177\3\177\3\177\3\177") + buf.write("\3\177\3\177\3\177\3\177\3\177\3\177\3\177\3\177\3\177") + buf.write("\3\177\3\177\3\177\3\177\3\177\3\177\3\177\3\177\3\177") + buf.write("\3\177\3\177\5\177\u04b2\n\177\3\177\3\177\3\177\3\177") + buf.write("\7\177\u04b8\n\177\f\177\16\177\u04bb\13\177\3\u0080\3") + buf.write("\u0080\3\u0080\3\u0080\3\u0081\3\u0081\3\u0081\3\u0081") + buf.write("\3\u0081\3\u0081\3\u0081\5\u0081\u04c8\n\u0081\3\u0082") + buf.write("\3\u0082\3\u0082\3\u0082\3\u0082\3\u0082\3\u0082\3\u0082") + buf.write("\3\u0082\5\u0082\u04d3\n\u0082\3\u0083\3\u0083\3\u0083") + buf.write("\3\u0083\7\u0083\u04d9\n\u0083\f\u0083\16\u0083\u04dc") + buf.write("\13\u0083\3\u0083\3\u0083\3\u0084\3\u0084\3\u0084\3\u0084") + buf.write("\3\u0084\3\u0084\3\u0084\3\u0084\3\u0084\3\u0084\3\u0084") + buf.write("\3\u0084\3\u0084\5\u0084\u04ed\n\u0084\3\u0085\3\u0085") + buf.write("\3\u0085\7\u0085\u04f2\n\u0085\f\u0085\16\u0085\u04f5") + buf.write("\13\u0085\3\u0085\3\u0085\3\u0086\3\u0086\3\u0086\7\u0086") + buf.write("\u04fc\n\u0086\f\u0086\16\u0086\u04ff\13\u0086\3\u0087") + buf.write("\3\u0087\3\u0087\3\u0087\3\u0087\5\u0087\u0506\n\u0087") + buf.write("\3\u0088\3\u0088\3\u0089\3\u0089\3\u0089\7\u0089\u050d") + buf.write("\n\u0089\f\u0089\16\u0089\u0510\13\u0089\3\u0089\3\u0089") + buf.write("\7\u0089\u0514\n\u0089\f\u0089\16\u0089\u0517\13\u0089") + buf.write("\3\u0089\3\u0089\3\u0089\7\u0089\u051c\n\u0089\f\u0089") + buf.write("\16\u0089\u051f\13\u0089\5\u0089\u0521\n\u0089\3\u008a") + buf.write("\3\u008a\3\u008b\3\u008b\3\u008b\3\u008b\3\u008c\3\u008c") + buf.write("\5\u008c\u052b\n\u008c\3\u008c\3\u008c\3\u008d\3\u008d") + buf.write("\3\u008d\2\7\n\u00ee\u00f2\u00f6\u00fc\u008e\2\4\6\b\n") + buf.write("\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62\64\668:<") + buf.write(">@BDFHJLNPRTVXZ\\^`bdfhjlnprtvxz|~\u0080\u0082\u0084\u0086") + buf.write("\u0088\u008a\u008c\u008e\u0090\u0092\u0094\u0096\u0098") + buf.write("\u009a\u009c\u009e\u00a0\u00a2\u00a4\u00a6\u00a8\u00aa") + buf.write("\u00ac\u00ae\u00b0\u00b2\u00b4\u00b6\u00b8\u00ba\u00bc") + buf.write("\u00be\u00c0\u00c2\u00c4\u00c6\u00c8\u00ca\u00cc\u00ce") + buf.write("\u00d0\u00d2\u00d4\u00d6\u00d8\u00da\u00dc\u00de\u00e0") + buf.write("\u00e2\u00e4\u00e6\u00e8\u00ea\u00ec\u00ee\u00f0\u00f2") + buf.write("\u00f4\u00f6\u00f8\u00fa\u00fc\u00fe\u0100\u0102\u0104") + buf.write("\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116") + buf.write("\u0118\2\13\3\2\16\25\3\2ef\3\2#\'\3\2\64\65\3\29;\4\2") + buf.write("\31\31NS\3\2TU\3\2VX\3\2eg\2\u0550\2\u011d\3\2\2\2\4\u0128") + buf.write("\3\2\2\2\6\u012f\3\2\2\2\b\u0133\3\2\2\2\n\u0135\3\2\2") + buf.write("\2\f\u014b\3\2\2\2\16\u014d\3\2\2\2\20\u0153\3\2\2\2\22") + buf.write("\u0155\3\2\2\2\24\u0157\3\2\2\2\26\u015c\3\2\2\2\30\u0164") + buf.write("\3\2\2\2\32\u0168\3\2\2\2\34\u016a\3\2\2\2\36\u0172\3") + buf.write("\2\2\2 \u0176\3\2\2\2\"\u0183\3\2\2\2$\u0189\3\2\2\2&") + buf.write("\u018f\3\2\2\2(\u0191\3\2\2\2*\u01a0\3\2\2\2,\u01a5\3") + buf.write("\2\2\2.\u01a7\3\2\2\2\60\u01a9\3\2\2\2\62\u01ab\3\2\2") + buf.write("\2\64\u01af\3\2\2\2\66\u01b8\3\2\2\28\u01ca\3\2\2\2:\u01d4") + buf.write("\3\2\2\2<\u01d6\3\2\2\2>\u01d8\3\2\2\2@\u01da\3\2\2\2") + buf.write("B\u01ec\3\2\2\2D\u01f6\3\2\2\2F\u01f8\3\2\2\2H\u01fa\3") + buf.write("\2\2\2J\u020d\3\2\2\2L\u0218\3\2\2\2N\u021d\3\2\2\2P\u0221") + buf.write("\3\2\2\2R\u0223\3\2\2\2T\u0236\3\2\2\2V\u023b\3\2\2\2") + buf.write("X\u0253\3\2\2\2Z\u0257\3\2\2\2\\\u0259\3\2\2\2^\u0268") + buf.write("\3\2\2\2`\u0276\3\2\2\2b\u027c\3\2\2\2d\u027e\3\2\2\2") + buf.write("f\u0293\3\2\2\2h\u0298\3\2\2\2j\u029a\3\2\2\2l\u029c\3") + buf.write("\2\2\2n\u02a0\3\2\2\2p\u02a2\3\2\2\2r\u02a4\3\2\2\2t\u02bb") + buf.write("\3\2\2\2v\u02bd\3\2\2\2x\u02c0\3\2\2\2z\u02c3\3\2\2\2") + buf.write("|\u02c5\3\2\2\2~\u02ca\3\2\2\2\u0080\u02d3\3\2\2\2\u0082") + buf.write("\u02d5\3\2\2\2\u0084\u02da\3\2\2\2\u0086\u02df\3\2\2\2") + buf.write("\u0088\u02e4\3\2\2\2\u008a\u02f0\3\2\2\2\u008c\u02f2\3") + buf.write("\2\2\2\u008e\u02f6\3\2\2\2\u0090\u02f8\3\2\2\2\u0092\u030a") + buf.write("\3\2\2\2\u0094\u031e\3\2\2\2\u0096\u0329\3\2\2\2\u0098") + buf.write("\u032b\3\2\2\2\u009a\u0338\3\2\2\2\u009c\u033c\3\2\2\2") + buf.write("\u009e\u033e\3\2\2\2\u00a0\u0347\3\2\2\2\u00a2\u0349\3") + buf.write("\2\2\2\u00a4\u034b\3\2\2\2\u00a6\u0353\3\2\2\2\u00a8\u035b") + buf.write("\3\2\2\2\u00aa\u0365\3\2\2\2\u00ac\u036c\3\2\2\2\u00ae") + buf.write("\u036e\3\2\2\2\u00b0\u037c\3\2\2\2\u00b2\u037e\3\2\2\2") + buf.write("\u00b4\u0384\3\2\2\2\u00b6\u038d\3\2\2\2\u00b8\u03a1\3") + buf.write("\2\2\2\u00ba\u03a6\3\2\2\2\u00bc\u03b2\3\2\2\2\u00be\u03c0") + buf.write("\3\2\2\2\u00c0\u03c2\3\2\2\2\u00c2\u03c4\3\2\2\2\u00c4") + buf.write("\u03c6\3\2\2\2\u00c6\u03ca\3\2\2\2\u00c8\u03d4\3\2\2\2") + buf.write("\u00ca\u03d8\3\2\2\2\u00cc\u03dc\3\2\2\2\u00ce\u03e3\3") + buf.write("\2\2\2\u00d0\u03f1\3\2\2\2\u00d2\u03f3\3\2\2\2\u00d4\u0404") + buf.write("\3\2\2\2\u00d6\u0406\3\2\2\2\u00d8\u040a\3\2\2\2\u00da") + buf.write("\u040c\3\2\2\2\u00dc\u041a\3\2\2\2\u00de\u043e\3\2\2\2") + buf.write("\u00e0\u0440\3\2\2\2\u00e2\u0444\3\2\2\2\u00e4\u0446\3") + buf.write("\2\2\2\u00e6\u044c\3\2\2\2\u00e8\u0454\3\2\2\2\u00ea\u045c") + buf.write("\3\2\2\2\u00ec\u0467\3\2\2\2\u00ee\u0469\3\2\2\2\u00f0") + buf.write("\u0475\3\2\2\2\u00f2\u0477\3\2\2\2\u00f4\u0483\3\2\2\2") + buf.write("\u00f6\u0485\3\2\2\2\u00f8\u0491\3\2\2\2\u00fa\u0496\3") + buf.write("\2\2\2\u00fc\u0498\3\2\2\2\u00fe\u04bc\3\2\2\2\u0100\u04c7") + buf.write("\3\2\2\2\u0102\u04d2\3\2\2\2\u0104\u04d4\3\2\2\2\u0106") + buf.write("\u04ec\3\2\2\2\u0108\u04f3\3\2\2\2\u010a\u04f8\3\2\2\2") + buf.write("\u010c\u0500\3\2\2\2\u010e\u0507\3\2\2\2\u0110\u0520\3") + buf.write("\2\2\2\u0112\u0522\3\2\2\2\u0114\u0524\3\2\2\2\u0116\u052a") + buf.write("\3\2\2\2\u0118\u052e\3\2\2\2\u011a\u011c\5\4\3\2\u011b") + buf.write("\u011a\3\2\2\2\u011c\u011f\3\2\2\2\u011d\u011b\3\2\2\2") + buf.write("\u011d\u011e\3\2\2\2\u011e\u0123\3\2\2\2\u011f\u011d\3") + buf.write("\2\2\2\u0120\u0122\5\f\7\2\u0121\u0120\3\2\2\2\u0122\u0125") + buf.write("\3\2\2\2\u0123\u0121\3\2\2\2\u0123\u0124\3\2\2\2\u0124") + buf.write("\u0126\3\2\2\2\u0125\u0123\3\2\2\2\u0126\u0127\7\2\2\3") + buf.write("\u0127\3\3\2\2\2\u0128\u0129\5\6\4\2\u0129\5\3\2\2\2\u012a") + buf.write("\u012b\7\3\2\2\u012b\u012c\5\b\5\2\u012c\u012d\7[\2\2") + buf.write("\u012d\u0130\3\2\2\2\u012e\u0130\7[\2\2\u012f\u012a\3") + buf.write("\2\2\2\u012f\u012e\3\2\2\2\u0130\7\3\2\2\2\u0131\u0134") + buf.write("\7c\2\2\u0132\u0134\5\n\6\2\u0133\u0131\3\2\2\2\u0133") + buf.write("\u0132\3\2\2\2\u0134\t\3\2\2\2\u0135\u0136\b\6\1\2\u0136") + buf.write("\u0137\7i\2\2\u0137\u013d\3\2\2\2\u0138\u0139\f\3\2\2") + buf.write("\u0139\u013a\7\4\2\2\u013a\u013c\7i\2\2\u013b\u0138\3") + buf.write("\2\2\2\u013c\u013f\3\2\2\2\u013d\u013b\3\2\2\2\u013d\u013e") + buf.write("\3\2\2\2\u013e\13\3\2\2\2\u013f\u013d\3\2\2\2\u0140\u014c") + buf.write("\5\16\b\2\u0141\u014c\5\26\f\2\u0142\u014c\5(\25\2\u0143") + buf.write("\u014c\5\66\34\2\u0144\u014c\5@!\2\u0145\u014c\5R*\2\u0146") + buf.write("\u014c\5H%\2\u0147\u014c\5V,\2\u0148\u014c\5Z.\2\u0149") + buf.write("\u014c\5d\63\2\u014a\u014c\7[\2\2\u014b\u0140\3\2\2\2") + buf.write("\u014b\u0141\3\2\2\2\u014b\u0142\3\2\2\2\u014b\u0143\3") + buf.write("\2\2\2\u014b\u0144\3\2\2\2\u014b\u0145\3\2\2\2\u014b\u0146") + buf.write("\3\2\2\2\u014b\u0147\3\2\2\2\u014b\u0148\3\2\2\2\u014b") + buf.write("\u0149\3\2\2\2\u014b\u014a\3\2\2\2\u014c\r\3\2\2\2\u014d") + buf.write("\u014e\7\5\2\2\u014e\u014f\5\20\t\2\u014f\u0150\7\6\2") + buf.write("\2\u0150\u0151\5\22\n\2\u0151\u0152\7[\2\2\u0152\17\3") + buf.write("\2\2\2\u0153\u0154\7i\2\2\u0154\21\3\2\2\2\u0155\u0156") + buf.write("\5\24\13\2\u0156\23\3\2\2\2\u0157\u0158\7\7\2\2\u0158") + buf.write("\u0159\7^\2\2\u0159\u015a\5\34\17\2\u015a\u015b\7_\2\2") + buf.write("\u015b\25\3\2\2\2\u015c\u015d\7\b\2\2\u015d\u015e\5\32") + buf.write("\16\2\u015e\u015f\7\t\2\2\u015f\u0160\5\20\t\2\u0160\u0161") + buf.write("\7\6\2\2\u0161\u0162\5\30\r\2\u0162\u0163\7[\2\2\u0163") + buf.write("\27\3\2\2\2\u0164\u0165\5 \21\2\u0165\31\3\2\2\2\u0166") + buf.write("\u0169\7i\2\2\u0167\u0169\5&\24\2\u0168\u0166\3\2\2\2") + buf.write("\u0168\u0167\3\2\2\2\u0169\33\3\2\2\2\u016a\u016f\5\36") + buf.write("\20\2\u016b\u016c\7\n\2\2\u016c\u016e\5\36\20\2\u016d") + buf.write("\u016b\3\2\2\2\u016e\u0171\3\2\2\2\u016f\u016d\3\2\2\2") + buf.write("\u016f\u0170\3\2\2\2\u0170\35\3\2\2\2\u0171\u016f\3\2") + buf.write("\2\2\u0172\u0173\5&\24\2\u0173\u0174\7\13\2\2\u0174\u0175") + buf.write("\5\u0118\u008d\2\u0175\37\3\2\2\2\u0176\u0177\7\7\2\2") + buf.write("\u0177\u0178\7^\2\2\u0178\u017b\5\34\17\2\u0179\u017a") + buf.write("\7\n\2\2\u017a\u017c\5\"\22\2\u017b\u0179\3\2\2\2\u017b") + buf.write("\u017c\3\2\2\2\u017c\u017f\3\2\2\2\u017d\u017e\7\n\2\2") + buf.write("\u017e\u0180\5$\23\2\u017f\u017d\3\2\2\2\u017f\u0180\3") + buf.write("\2\2\2\u0180\u0181\3\2\2\2\u0181\u0182\7_\2\2\u0182!\3") + buf.write("\2\2\2\u0183\u0184\7\f\2\2\u0184\u0187\7\13\2\2\u0185") + buf.write("\u0188\7d\2\2\u0186\u0188\5\u0118\u008d\2\u0187\u0185") + buf.write("\3\2\2\2\u0187\u0186\3\2\2\2\u0188#\3\2\2\2\u0189\u018a") + buf.write("\7\r\2\2\u018a\u018d\7\13\2\2\u018b\u018e\7d\2\2\u018c") + buf.write("\u018e\5\u0118\u008d\2\u018d\u018b\3\2\2\2\u018d\u018c") + buf.write("\3\2\2\2\u018e%\3\2\2\2\u018f\u0190\t\2\2\2\u0190\'\3") + buf.write("\2\2\2\u0191\u0192\7\26\2\2\u0192\u0193\5.\30\2\u0193") + buf.write("\u0194\7\13\2\2\u0194\u0195\7\\\2\2\u0195\u019a\5*\26") + buf.write("\2\u0196\u0197\7\n\2\2\u0197\u0199\5*\26\2\u0198\u0196") + buf.write("\3\2\2\2\u0199\u019c\3\2\2\2\u019a\u0198\3\2\2\2\u019a") + buf.write("\u019b\3\2\2\2\u019b\u019d\3\2\2\2\u019c\u019a\3\2\2\2") + buf.write("\u019d\u019e\7]\2\2\u019e\u019f\7[\2\2\u019f)\3\2\2\2") + buf.write("\u01a0\u01a3\5\60\31\2\u01a1\u01a2\7\27\2\2\u01a2\u01a4") + buf.write("\5,\27\2\u01a3\u01a1\3\2\2\2\u01a3\u01a4\3\2\2\2\u01a4") + buf.write("+\3\2\2\2\u01a5\u01a6\t\3\2\2\u01a6-\3\2\2\2\u01a7\u01a8") + buf.write("\7i\2\2\u01a8/\3\2\2\2\u01a9\u01aa\7i\2\2\u01aa\61\3\2") + buf.write("\2\2\u01ab\u01ac\5.\30\2\u01ac\u01ad\7\30\2\2\u01ad\u01ae") + buf.write("\5\60\31\2\u01ae\63\3\2\2\2\u01af\u01b0\7^\2\2\u01b0\u01b1") + buf.write("\5<\37\2\u01b1\u01b4\7\31\2\2\u01b2\u01b5\5\62\32\2\u01b3") + buf.write("\u01b5\7h\2\2\u01b4\u01b2\3\2\2\2\u01b4\u01b3\3\2\2\2") + buf.write("\u01b5\u01b6\3\2\2\2\u01b6\u01b7\7_\2\2\u01b7\65\3\2\2") + buf.write("\2\u01b8\u01b9\7\32\2\2\u01b9\u01bb\5> \2\u01ba\u01bc") + buf.write("\58\35\2\u01bb\u01ba\3\2\2\2\u01bb\u01bc\3\2\2\2\u01bc") + buf.write("\u01c8\3\2\2\2\u01bd\u01be\7\13\2\2\u01be\u01bf\7[\2\2") + buf.write("\u01bf\u01c1\7j\2\2\u01c0\u01c2\5:\36\2\u01c1\u01c0\3") + buf.write("\2\2\2\u01c2\u01c3\3\2\2\2\u01c3\u01c1\3\2\2\2\u01c3\u01c4") + buf.write("\3\2\2\2\u01c4\u01c5\3\2\2\2\u01c5\u01c6\7k\2\2\u01c6") + buf.write("\u01c9\3\2\2\2\u01c7\u01c9\7[\2\2\u01c8\u01bd\3\2\2\2") + buf.write("\u01c8\u01c7\3\2\2\2\u01c9\67\3\2\2\2\u01ca\u01cb\7\33") + buf.write("\2\2\u01cb\u01cd\5> \2\u01cc\u01ce\5\64\33\2\u01cd\u01cc") + buf.write("\3\2\2\2\u01cd\u01ce\3\2\2\2\u01ce9\3\2\2\2\u01cf\u01d5") + buf.write("\5r:\2\u01d0\u01d5\5\u008eH\2\u01d1\u01d5\5\u009cO\2\u01d2") + buf.write("\u01d5\5\u00ceh\2\u01d3\u01d5\5\u00d8m\2\u01d4\u01cf\3") + buf.write("\2\2\2\u01d4\u01d0\3\2\2\2\u01d4\u01d1\3\2\2\2\u01d4\u01d2") + buf.write("\3\2\2\2\u01d4\u01d3\3\2\2\2\u01d5;\3\2\2\2\u01d6\u01d7") + buf.write("\7i\2\2\u01d7=\3\2\2\2\u01d8\u01d9\7i\2\2\u01d9?\3\2\2") + buf.write("\2\u01da\u01db\7\34\2\2\u01db\u01dd\5F$\2\u01dc\u01de") + buf.write("\5B\"\2\u01dd\u01dc\3\2\2\2\u01dd\u01de\3\2\2\2\u01de") + buf.write("\u01ea\3\2\2\2\u01df\u01e0\7\13\2\2\u01e0\u01e1\7[\2\2") + buf.write("\u01e1\u01e3\7j\2\2\u01e2\u01e4\5D#\2\u01e3\u01e2\3\2") + buf.write("\2\2\u01e4\u01e5\3\2\2\2\u01e5\u01e3\3\2\2\2\u01e5\u01e6") + buf.write("\3\2\2\2\u01e6\u01e7\3\2\2\2\u01e7\u01e8\7k\2\2\u01e8") + buf.write("\u01eb\3\2\2\2\u01e9\u01eb\7[\2\2\u01ea\u01df\3\2\2\2") + buf.write("\u01ea\u01e9\3\2\2\2\u01ebA\3\2\2\2\u01ec\u01ed\7\33\2") + buf.write("\2\u01ed\u01ef\5F$\2\u01ee\u01f0\5\64\33\2\u01ef\u01ee") + buf.write("\3\2\2\2\u01ef\u01f0\3\2\2\2\u01f0C\3\2\2\2\u01f1\u01f7") + buf.write("\5r:\2\u01f2\u01f7\5\u008eH\2\u01f3\u01f7\5\u009cO\2\u01f4") + buf.write("\u01f7\5\u00ceh\2\u01f5\u01f7\5\u00d8m\2\u01f6\u01f1\3") + buf.write("\2\2\2\u01f6\u01f2\3\2\2\2\u01f6\u01f3\3\2\2\2\u01f6\u01f4") + buf.write("\3\2\2\2\u01f6\u01f5\3\2\2\2\u01f7E\3\2\2\2\u01f8\u01f9") + buf.write("\7i\2\2\u01f9G\3\2\2\2\u01fa\u01fb\7\35\2\2\u01fb\u01fd") + buf.write("\5N(\2\u01fc\u01fe\5J&\2\u01fd\u01fc\3\2\2\2\u01fd\u01fe") + buf.write("\3\2\2\2\u01fe\u020b\3\2\2\2\u01ff\u0200\7\13\2\2\u0200") + buf.write("\u0201\7[\2\2\u0201\u0204\7j\2\2\u0202\u0205\5L\'\2\u0203") + buf.write("\u0205\5\u00acW\2\u0204\u0202\3\2\2\2\u0204\u0203\3\2") + buf.write("\2\2\u0205\u0206\3\2\2\2\u0206\u0204\3\2\2\2\u0206\u0207") + buf.write("\3\2\2\2\u0207\u0208\3\2\2\2\u0208\u0209\7k\2\2\u0209") + buf.write("\u020c\3\2\2\2\u020a\u020c\7[\2\2\u020b\u01ff\3\2\2\2") + buf.write("\u020b\u020a\3\2\2\2\u020cI\3\2\2\2\u020d\u020e\7\33\2") + buf.write("\2\u020e\u0210\5N(\2\u020f\u0211\5\64\33\2\u0210\u020f") + buf.write("\3\2\2\2\u0210\u0211\3\2\2\2\u0211K\3\2\2\2\u0212\u0219") + buf.write("\5r:\2\u0213\u0219\5\u008eH\2\u0214\u0219\5\u009cO\2\u0215") + buf.write("\u0219\5\u00ceh\2\u0216\u0219\5\u00d8m\2\u0217\u0219\5") + buf.write("\u00a8U\2\u0218\u0212\3\2\2\2\u0218\u0213\3\2\2\2\u0218") + buf.write("\u0214\3\2\2\2\u0218\u0215\3\2\2\2\u0218\u0216\3\2\2\2") + buf.write("\u0218\u0217\3\2\2\2\u0219M\3\2\2\2\u021a\u021b\5F$\2") + buf.write("\u021b\u021c\7\4\2\2\u021c\u021e\3\2\2\2\u021d\u021a\3") + buf.write("\2\2\2\u021d\u021e\3\2\2\2\u021e\u021f\3\2\2\2\u021f\u0220") + buf.write("\5P)\2\u0220O\3\2\2\2\u0221\u0222\7i\2\2\u0222Q\3\2\2") + buf.write("\2\u0223\u0224\7\36\2\2\u0224\u0226\5N(\2\u0225\u0227") + buf.write("\5T+\2\u0226\u0225\3\2\2\2\u0226\u0227\3\2\2\2\u0227\u0234") + buf.write("\3\2\2\2\u0228\u0229\7\13\2\2\u0229\u022a\7[\2\2\u022a") + buf.write("\u022d\7j\2\2\u022b\u022e\5L\'\2\u022c\u022e\5\u00acW") + buf.write("\2\u022d\u022b\3\2\2\2\u022d\u022c\3\2\2\2\u022e\u022f") + buf.write("\3\2\2\2\u022f\u022d\3\2\2\2\u022f\u0230\3\2\2\2\u0230") + buf.write("\u0231\3\2\2\2\u0231\u0232\7k\2\2\u0232\u0235\3\2\2\2") + buf.write("\u0233\u0235\7[\2\2\u0234\u0228\3\2\2\2\u0234\u0233\3") + buf.write("\2\2\2\u0235S\3\2\2\2\u0236\u0237\7\33\2\2\u0237\u0239") + buf.write("\5N(\2\u0238\u023a\5\64\33\2\u0239\u0238\3\2\2\2\u0239") + buf.write("\u023a\3\2\2\2\u023aU\3\2\2\2\u023b\u023f\7\37\2\2\u023c") + buf.write("\u023d\5F$\2\u023d\u023e\7\4\2\2\u023e\u0240\3\2\2\2\u023f") + buf.write("\u023c\3\2\2\2\u023f\u0240\3\2\2\2\u0240\u0241\3\2\2\2") + buf.write("\u0241\u0244\5X-\2\u0242\u0243\7\t\2\2\u0243\u0245\5N") + buf.write("(\2\u0244\u0242\3\2\2\2\u0244\u0245\3\2\2\2\u0245\u0251") + buf.write("\3\2\2\2\u0246\u0247\7\13\2\2\u0247\u0248\7[\2\2\u0248") + buf.write("\u024a\7j\2\2\u0249\u024b\5L\'\2\u024a\u0249\3\2\2\2\u024b") + buf.write("\u024c\3\2\2\2\u024c\u024a\3\2\2\2\u024c\u024d\3\2\2\2") + buf.write("\u024d\u024e\3\2\2\2\u024e\u024f\7k\2\2\u024f\u0252\3") + buf.write("\2\2\2\u0250\u0252\7[\2\2\u0251\u0246\3\2\2\2\u0251\u0250") + buf.write("\3\2\2\2\u0252W\3\2\2\2\u0253\u0254\7i\2\2\u0254Y\3\2") + buf.write("\2\2\u0255\u0258\5\\/\2\u0256\u0258\5^\60\2\u0257\u0255") + buf.write("\3\2\2\2\u0257\u0256\3\2\2\2\u0258[\3\2\2\2\u0259\u025a") + buf.write("\7 \2\2\u025a\u025b\5.\30\2\u025b\u025c\7\13\2\2\u025c") + buf.write("\u025d\7\\\2\2\u025d\u0262\5*\26\2\u025e\u025f\7\n\2\2") + buf.write("\u025f\u0261\5*\26\2\u0260\u025e\3\2\2\2\u0261\u0264\3") + buf.write("\2\2\2\u0262\u0260\3\2\2\2\u0262\u0263\3\2\2\2\u0263\u0265") + buf.write("\3\2\2\2\u0264\u0262\3\2\2\2\u0265\u0266\7]\2\2\u0266") + buf.write("\u0267\7[\2\2\u0267]\3\2\2\2\u0268\u0269\7 \2\2\u0269") + buf.write("\u026a\5`\61\2\u026a\u026b\7\13\2\2\u026b\u026c\7[\2\2") + buf.write("\u026c\u026e\7j\2\2\u026d\u026f\5b\62\2\u026e\u026d\3") + buf.write("\2\2\2\u026f\u0270\3\2\2\2\u0270\u026e\3\2\2\2\u0270\u0271") + buf.write("\3\2\2\2\u0271\u0272\3\2\2\2\u0272\u0273\7k\2\2\u0273") + buf.write("_\3\2\2\2\u0274\u0277\5p9\2\u0275\u0277\5N(\2\u0276\u0274") + buf.write("\3\2\2\2\u0276\u0275\3\2\2\2\u0277a\3\2\2\2\u0278\u027d") + buf.write("\5:\36\2\u0279\u027d\5D#\2\u027a\u027d\5L\'\2\u027b\u027d") + buf.write("\5\u00acW\2\u027c\u0278\3\2\2\2\u027c\u0279\3\2\2\2\u027c") + buf.write("\u027a\3\2\2\2\u027c\u027b\3\2\2\2\u027dc\3\2\2\2\u027e") + buf.write("\u027f\7!\2\2\u027f\u0284\5<\37\2\u0280\u0281\7\n\2\2") + buf.write("\u0281\u0283\5<\37\2\u0282\u0280\3\2\2\2\u0283\u0286\3") + buf.write("\2\2\2\u0284\u0282\3\2\2\2\u0284\u0285\3\2\2\2\u0285\u0287") + buf.write("\3\2\2\2\u0286\u0284\3\2\2\2\u0287\u0288\7\13\2\2\u0288") + buf.write("\u028b\5f\64\2\u0289\u028a\7\27\2\2\u028a\u028c\5\u0096") + buf.write("L\2\u028b\u0289\3\2\2\2\u028b\u028c\3\2\2\2\u028c\u028f") + buf.write("\3\2\2\2\u028d\u0290\5\u0098M\2\u028e\u0290\7[\2\2\u028f") + buf.write("\u028d\3\2\2\2\u028f\u028e\3\2\2\2\u0290e\3\2\2\2\u0291") + buf.write("\u0294\5h\65\2\u0292\u0294\5j\66\2\u0293\u0291\3\2\2\2") + buf.write("\u0293\u0292\3\2\2\2\u0294g\3\2\2\2\u0295\u0299\5n8\2") + buf.write("\u0296\u0299\5p9\2\u0297\u0299\5N(\2\u0298\u0295\3\2\2") + buf.write("\2\u0298\u0296\3\2\2\2\u0298\u0297\3\2\2\2\u0299i\3\2") + buf.write("\2\2\u029a\u029b\5l\67\2\u029bk\3\2\2\2\u029c\u029d\7") + buf.write("\"\2\2\u029d\u029e\7\t\2\2\u029e\u029f\5h\65\2\u029fm") + buf.write("\3\2\2\2\u02a0\u02a1\t\4\2\2\u02a1o\3\2\2\2\u02a2\u02a3") + buf.write("\7i\2\2\u02a3q\3\2\2\2\u02a4\u02a5\7(\2\2\u02a5\u02aa") + buf.write("\5|?\2\u02a6\u02a7\7^\2\2\u02a7\u02a8\5\u010a\u0086\2") + buf.write("\u02a8\u02a9\7_\2\2\u02a9\u02ab\3\2\2\2\u02aa\u02a6\3") + buf.write("\2\2\2\u02aa\u02ab\3\2\2\2\u02ab\u02ae\3\2\2\2\u02ac\u02ad") + buf.write("\7\6\2\2\u02ad\u02af\5t;\2\u02ae\u02ac\3\2\2\2\u02ae\u02af") + buf.write("\3\2\2\2\u02af\u02b0\3\2\2\2\u02b0\u02b1\7[\2\2\u02b1") + buf.write("s\3\2\2\2\u02b2\u02b8\5v<\2\u02b3\u02b5\5x=\2\u02b4\u02b3") + buf.write("\3\2\2\2\u02b4\u02b5\3\2\2\2\u02b5\u02b6\3\2\2\2\u02b6") + buf.write("\u02b7\7)\2\2\u02b7\u02b9\5\u0080A\2\u02b8\u02b4\3\2\2") + buf.write("\2\u02b8\u02b9\3\2\2\2\u02b9\u02bc\3\2\2\2\u02ba\u02bc") + buf.write("\5\u0080A\2\u02bb\u02b2\3\2\2\2\u02bb\u02ba\3\2\2\2\u02bc") + buf.write("u\3\2\2\2\u02bd\u02be\7*\2\2\u02be\u02bf\5~@\2\u02bfw") + buf.write("\3\2\2\2\u02c0\u02c1\7+\2\2\u02c1\u02c2\5z>\2\u02c2y\3") + buf.write("\2\2\2\u02c3\u02c4\7i\2\2\u02c4{\3\2\2\2\u02c5\u02c6\7") + buf.write("i\2\2\u02c6}\3\2\2\2\u02c7\u02c8\5\u00e2r\2\u02c8\u02c9") + buf.write("\7\4\2\2\u02c9\u02cb\3\2\2\2\u02ca\u02c7\3\2\2\2\u02ca") + buf.write("\u02cb\3\2\2\2\u02cb\u02cc\3\2\2\2\u02cc\u02cd\5|?\2\u02cd") + buf.write("\177\3\2\2\2\u02ce\u02d4\5\u008aF\2\u02cf\u02d4\5\u0082") + buf.write("B\2\u02d0\u02d4\5\u0084C\2\u02d1\u02d4\5\u0086D\2\u02d2") + buf.write("\u02d4\5\u0088E\2\u02d3\u02ce\3\2\2\2\u02d3\u02cf\3\2") + buf.write("\2\2\u02d3\u02d0\3\2\2\2\u02d3\u02d1\3\2\2\2\u02d3\u02d2") + buf.write("\3\2\2\2\u02d4\u0081\3\2\2\2\u02d5\u02d6\7,\2\2\u02d6") + buf.write("\u02d7\7^\2\2\u02d7\u02d8\5\u008aF\2\u02d8\u02d9\7_\2") + buf.write("\2\u02d9\u0083\3\2\2\2\u02da\u02db\7-\2\2\u02db\u02dc") + buf.write("\7^\2\2\u02dc\u02dd\5\u008aF\2\u02dd\u02de\7_\2\2\u02de") + buf.write("\u0085\3\2\2\2\u02df\u02e0\7.\2\2\u02e0\u02e1\7^\2\2\u02e1") + buf.write("\u02e2\5\u008cG\2\u02e2\u02e3\7_\2\2\u02e3\u0087\3\2\2") + buf.write("\2\u02e4\u02e5\7/\2\2\u02e5\u02e6\7^\2\2\u02e6\u02ec\5") + buf.write("\u008cG\2\u02e7\u02e8\7\n\2\2\u02e8\u02e9\7i\2\2\u02e9") + buf.write("\u02ea\bE\1\2\u02ea\u02eb\7\13\2\2\u02eb\u02ed\5\u008c") + buf.write("G\2\u02ec\u02e7\3\2\2\2\u02ec\u02ed\3\2\2\2\u02ed\u02ee") + buf.write("\3\2\2\2\u02ee\u02ef\7_\2\2\u02ef\u0089\3\2\2\2\u02f0") + buf.write("\u02f1\5\u00e2r\2\u02f1\u008b\3\2\2\2\u02f2\u02f3\5\u00e2") + buf.write("r\2\u02f3\u008d\3\2\2\2\u02f4\u02f7\5\u0090I\2\u02f5\u02f7") + buf.write("\5\u0092J\2\u02f6\u02f4\3\2\2\2\u02f6\u02f5\3\2\2\2\u02f7") + buf.write("\u008f\3\2\2\2\u02f8\u02fd\5<\37\2\u02f9\u02fa\7\n\2\2") + buf.write("\u02fa\u02fc\5<\37\2\u02fb\u02f9\3\2\2\2\u02fc\u02ff\3") + buf.write("\2\2\2\u02fd\u02fb\3\2\2\2\u02fd\u02fe\3\2\2\2\u02fe\u0300") + buf.write("\3\2\2\2\u02ff\u02fd\3\2\2\2\u0300\u0301\7\13\2\2\u0301") + buf.write("\u0304\5f\64\2\u0302\u0303\7\27\2\2\u0303\u0305\5\u0096") + buf.write("L\2\u0304\u0302\3\2\2\2\u0304\u0305\3\2\2\2\u0305\u0308") + buf.write("\3\2\2\2\u0306\u0309\5\u0098M\2\u0307\u0309\7[\2\2\u0308") + buf.write("\u0306\3\2\2\2\u0308\u0307\3\2\2\2\u0309\u0091\3\2\2\2") + buf.write("\u030a\u030b\7\60\2\2\u030b\u0310\5<\37\2\u030c\u030d") + buf.write("\7\n\2\2\u030d\u030f\5<\37\2\u030e\u030c\3\2\2\2\u030f") + buf.write("\u0312\3\2\2\2\u0310\u030e\3\2\2\2\u0310\u0311\3\2\2\2") + buf.write("\u0311\u0313\3\2\2\2\u0312\u0310\3\2\2\2\u0313\u0314\7") + buf.write("\13\2\2\u0314\u031a\5f\64\2\u0315\u0318\7\27\2\2\u0316") + buf.write("\u0319\5\u0094K\2\u0317\u0319\5\u0102\u0082\2\u0318\u0316") + buf.write("\3\2\2\2\u0318\u0317\3\2\2\2\u0319\u031b\3\2\2\2\u031a") + buf.write("\u0315\3\2\2\2\u031a\u031b\3\2\2\2\u031b\u031c\3\2\2\2") + buf.write("\u031c\u031d\7[\2\2\u031d\u0093\3\2\2\2\u031e\u031f\7") + buf.write("\61\2\2\u031f\u0320\7^\2\2\u0320\u0321\5\u00e2r\2\u0321") + buf.write("\u0322\7\n\2\2\u0322\u0325\5t;\2\u0323\u0324\7\n\2\2\u0324") + buf.write("\u0326\5\u0096L\2\u0325\u0323\3\2\2\2\u0325\u0326\3\2") + buf.write("\2\2\u0326\u0327\3\2\2\2\u0327\u0328\7_\2\2\u0328\u0095") + buf.write("\3\2\2\2\u0329\u032a\5\u00e2r\2\u032a\u0097\3\2\2\2\u032b") + buf.write("\u032c\7\62\2\2\u032c\u032d\7\13\2\2\u032d\u032e\7[\2") + buf.write("\2\u032e\u0330\7j\2\2\u032f\u0331\5\u009aN\2\u0330\u032f") + buf.write("\3\2\2\2\u0331\u0332\3\2\2\2\u0332\u0330\3\2\2\2\u0332") + buf.write("\u0333\3\2\2\2\u0333\u0334\3\2\2\2\u0334\u0335\7k\2\2") + buf.write("\u0335\u0099\3\2\2\2\u0336\u0339\5\u009cO\2\u0337\u0339") + buf.write("\5\u00d8m\2\u0338\u0336\3\2\2\2\u0338\u0337\3\2\2\2\u0339") + buf.write("\u009b\3\2\2\2\u033a\u033d\5\u009eP\2\u033b\u033d\5\u00a4") + buf.write("S\2\u033c\u033a\3\2\2\2\u033c\u033b\3\2\2\2\u033d\u009d") + buf.write("\3\2\2\2\u033e\u033f\7\63\2\2\u033f\u0341\7^\2\2\u0340") + buf.write("\u0342\5\u00a0Q\2\u0341\u0340\3\2\2\2\u0341\u0342\3\2") + buf.write("\2\2\u0342\u0343\3\2\2\2\u0343\u0344\5\u00a2R\2\u0344") + buf.write("\u0345\7_\2\2\u0345\u0346\7[\2\2\u0346\u009f\3\2\2\2\u0347") + buf.write("\u0348\t\5\2\2\u0348\u00a1\3\2\2\2\u0349\u034a\5\u00e2") + buf.write("r\2\u034a\u00a3\3\2\2\2\u034b\u034c\7\66\2\2\u034c\u034d") + buf.write("\7^\2\2\u034d\u034e\5\u00a6T\2\u034e\u034f\7_\2\2\u034f") + buf.write("\u0350\7[\2\2\u0350\u00a5\3\2\2\2\u0351\u0354\5<\37\2") + buf.write("\u0352\u0354\5\u00fe\u0080\2\u0353\u0351\3\2\2\2\u0353") + buf.write("\u0352\3\2\2\2\u0354\u00a7\3\2\2\2\u0355\u0358\5\u00aa") + buf.write("V\2\u0356\u0358\5\u00c2b\2\u0357\u0355\3\2\2\2\u0357\u0356") + buf.write("\3\2\2\2\u0358\u0359\3\2\2\2\u0359\u035a\7\4\2\2\u035a") + buf.write("\u035c\3\2\2\2\u035b\u0357\3\2\2\2\u035b\u035c\3\2\2\2") + buf.write("\u035c\u035d\3\2\2\2\u035d\u035e\5X-\2\u035e\u0360\7^") + buf.write("\2\2\u035f\u0361\5\u0110\u0089\2\u0360\u035f\3\2\2\2\u0360") + buf.write("\u0361\3\2\2\2\u0361\u0362\3\2\2\2\u0362\u0363\7_\2\2") + buf.write("\u0363\u0364\7[\2\2\u0364\u00a9\3\2\2\2\u0365\u0366\5") + buf.write("\u00c2b\2\u0366\u0367\7\4\2\2\u0367\u0368\3\2\2\2\u0368") + buf.write("\u0369\5P)\2\u0369\u00ab\3\2\2\2\u036a\u036d\5\u00aeX") + buf.write("\2\u036b\u036d\5\u00b2Z\2\u036c\u036a\3\2\2\2\u036c\u036b") + buf.write("\3\2\2\2\u036d\u00ad\3\2\2\2\u036e\u036f\7\67\2\2\u036f") + buf.write("\u0370\5t;\2\u0370\u0371\7\13\2\2\u0371\u0372\7[\2\2\u0372") + buf.write("\u0374\7j\2\2\u0373\u0375\5\u00b0Y\2\u0374\u0373\3\2\2") + buf.write("\2\u0375\u0376\3\2\2\2\u0376\u0374\3\2\2\2\u0376\u0377") + buf.write("\3\2\2\2\u0377\u0378\3\2\2\2\u0378\u0379\7k\2\2\u0379") + buf.write("\u00af\3\2\2\2\u037a\u037d\5\u00c8e\2\u037b\u037d\5\u00c6") + buf.write("d\2\u037c\u037a\3\2\2\2\u037c\u037b\3\2\2\2\u037d\u00b1") + buf.write("\3\2\2\2\u037e\u037f\78\2\2\u037f\u0380\5\u00b4[\2\u0380") + buf.write("\u00b3\3\2\2\2\u0381\u0382\5\u00c0a\2\u0382\u0383\7\13") + buf.write("\2\2\u0383\u0385\3\2\2\2\u0384\u0381\3\2\2\2\u0384\u0385") + buf.write("\3\2\2\2\u0385\u038b\3\2\2\2\u0386\u038c\5\u00b6\\\2\u0387") + buf.write("\u038c\5\u00ba^\2\u0388\u038c\5\u00c4c\2\u0389\u038c\5") + buf.write("\u00c6d\2\u038a\u038c\5\u00c8e\2\u038b\u0386\3\2\2\2\u038b") + buf.write("\u0387\3\2\2\2\u038b\u0388\3\2\2\2\u038b\u0389\3\2\2\2") + buf.write("\u038b\u038a\3\2\2\2\u038c\u00b5\3\2\2\2\u038d\u0393\5") + buf.write("\u00b8]\2\u038e\u0390\7^\2\2\u038f\u0391\5\u0110\u0089") + buf.write("\2\u0390\u038f\3\2\2\2\u0390\u0391\3\2\2\2\u0391\u0392") + buf.write("\3\2\2\2\u0392\u0394\7_\2\2\u0393\u038e\3\2\2\2\u0393") + buf.write("\u0394\3\2\2\2\u0394\u0395\3\2\2\2\u0395\u0396\7\13\2") + buf.write("\2\u0396\u0397\7[\2\2\u0397\u0399\7j\2\2\u0398\u039a\5") + buf.write("\u00b4[\2\u0399\u0398\3\2\2\2\u039a\u039b\3\2\2\2\u039b") + buf.write("\u0399\3\2\2\2\u039b\u039c\3\2\2\2\u039c\u039d\3\2\2\2") + buf.write("\u039d\u039f\7k\2\2\u039e\u03a0\5\u00bc_\2\u039f\u039e") + buf.write("\3\2\2\2\u039f\u03a0\3\2\2\2\u03a0\u00b7\3\2\2\2\u03a1") + buf.write("\u03a2\t\6\2\2\u03a2\u00b9\3\2\2\2\u03a3\u03a4\5\u00c2") + buf.write("b\2\u03a4\u03a5\7\4\2\2\u03a5\u03a7\3\2\2\2\u03a6\u03a3") + buf.write("\3\2\2\2\u03a6\u03a7\3\2\2\2\u03a7\u03a8\3\2\2\2\u03a8") + buf.write("\u03a9\5P)\2\u03a9\u03ab\7^\2\2\u03aa\u03ac\5\u0110\u0089") + buf.write("\2\u03ab\u03aa\3\2\2\2\u03ab\u03ac\3\2\2\2\u03ac\u03ad") + buf.write("\3\2\2\2\u03ad\u03b0\7_\2\2\u03ae\u03b1\5\u00bc_\2\u03af") + buf.write("\u03b1\7[\2\2\u03b0\u03ae\3\2\2\2\u03b0\u03af\3\2\2\2") + buf.write("\u03b1\u00bb\3\2\2\2\u03b2\u03b3\7\62\2\2\u03b3\u03b4") + buf.write("\7\13\2\2\u03b4\u03b5\7[\2\2\u03b5\u03b7\7j\2\2\u03b6") + buf.write("\u03b8\5\u00be`\2\u03b7\u03b6\3\2\2\2\u03b8\u03b9\3\2") + buf.write("\2\2\u03b9\u03b7\3\2\2\2\u03b9\u03ba\3\2\2\2\u03ba\u03bb") + buf.write("\3\2\2\2\u03bb\u03bc\7k\2\2\u03bc\u00bd\3\2\2\2\u03bd") + buf.write("\u03c1\5\u009cO\2\u03be\u03c1\5\u00a8U\2\u03bf\u03c1\5") + buf.write("\u00caf\2\u03c0\u03bd\3\2\2\2\u03c0\u03be\3\2\2\2\u03c0") + buf.write("\u03bf\3\2\2\2\u03c1\u00bf\3\2\2\2\u03c2\u03c3\7i\2\2") + buf.write("\u03c3\u00c1\3\2\2\2\u03c4\u03c5\5F$\2\u03c5\u00c3\3\2") + buf.write("\2\2\u03c6\u03c7\7<\2\2\u03c7\u03c8\5t;\2\u03c8\u03c9") + buf.write("\7[\2\2\u03c9\u00c5\3\2\2\2\u03ca\u03cb\7=\2\2\u03cb\u03d0") + buf.write("\5|?\2\u03cc\u03cd\7^\2\2\u03cd\u03ce\5\u0110\u0089\2") + buf.write("\u03ce\u03cf\7_\2\2\u03cf\u03d1\3\2\2\2\u03d0\u03cc\3") + buf.write("\2\2\2\u03d0\u03d1\3\2\2\2\u03d1\u03d2\3\2\2\2\u03d2\u03d3") + buf.write("\7[\2\2\u03d3\u00c7\3\2\2\2\u03d4\u03d5\7>\2\2\u03d5\u03d6") + buf.write("\5\u00ccg\2\u03d6\u03d7\7[\2\2\u03d7\u00c9\3\2\2\2\u03d8") + buf.write("\u03d9\7?\2\2\u03d9\u03da\5t;\2\u03da\u03db\7[\2\2\u03db") + buf.write("\u00cb\3\2\2\2\u03dc\u03dd\5\u00fc\177\2\u03dd\u03df\7") + buf.write("^\2\2\u03de\u03e0\5\u0110\u0089\2\u03df\u03de\3\2\2\2") + buf.write("\u03df\u03e0\3\2\2\2\u03e0\u03e1\3\2\2\2\u03e1\u03e2\7") + buf.write("_\2\2\u03e2\u00cd\3\2\2\2\u03e3\u03e4\7@\2\2\u03e4\u03e5") + buf.write("\5\u00d6l\2\u03e5\u03e7\7^\2\2\u03e6\u03e8\5\u010a\u0086") + buf.write("\2\u03e7\u03e6\3\2\2\2\u03e7\u03e8\3\2\2\2\u03e8\u03e9") + buf.write("\3\2\2\2\u03e9\u03ec\7_\2\2\u03ea\u03eb\7A\2\2\u03eb\u03ed") + buf.write("\5\u00d0i\2\u03ec\u03ea\3\2\2\2\u03ec\u03ed\3\2\2\2\u03ed") + buf.write("\u03ee\3\2\2\2\u03ee\u03ef\5\u00d2j\2\u03ef\u03f0\7[\2") + buf.write("\2\u03f0\u00cf\3\2\2\2\u03f1\u03f2\5f\64\2\u03f2\u00d1") + buf.write("\3\2\2\2\u03f3\u03f5\7\6\2\2\u03f4\u03f6\5\u00d4k\2\u03f5") + buf.write("\u03f4\3\2\2\2\u03f5\u03f6\3\2\2\2\u03f6\u0402\3\2\2\2") + buf.write("\u03f7\u03f8\7B\2\2\u03f8\u0403\5\u00e2r\2\u03f9\u0403") + buf.write("\7C\2\2\u03fa\u03fb\7D\2\2\u03fb\u03fc\5\n\6\2\u03fc\u03fe") + buf.write("\7^\2\2\u03fd\u03ff\5\u0110\u0089\2\u03fe\u03fd\3\2\2") + buf.write("\2\u03fe\u03ff\3\2\2\2\u03ff\u0400\3\2\2\2\u0400\u0401") + buf.write("\7_\2\2\u0401\u0403\3\2\2\2\u0402\u03f7\3\2\2\2\u0402") + buf.write("\u03f9\3\2\2\2\u0402\u03fa\3\2\2\2\u0403\u00d3\3\2\2\2") + buf.write("\u0404\u0405\7E\2\2\u0405\u00d5\3\2\2\2\u0406\u0407\7") + buf.write("i\2\2\u0407\u00d7\3\2\2\2\u0408\u040b\5\u00dan\2\u0409") + buf.write("\u040b\5\u00dco\2\u040a\u0408\3\2\2\2\u040a\u0409\3\2") + buf.write("\2\2\u040b\u00d9\3\2\2\2\u040c\u040d\7F\2\2\u040d\u040f") + buf.write("\7^\2\2\u040e\u0410\5\u00e0q\2\u040f\u040e\3\2\2\2\u040f") + buf.write("\u0410\3\2\2\2\u0410\u0414\3\2\2\2\u0411\u0413\5\u00de") + buf.write("p\2\u0412\u0411\3\2\2\2\u0413\u0416\3\2\2\2\u0414\u0412") + buf.write("\3\2\2\2\u0414\u0415\3\2\2\2\u0415\u0417\3\2\2\2\u0416") + buf.write("\u0414\3\2\2\2\u0417\u0418\7_\2\2\u0418\u0419\7[\2\2\u0419") + buf.write("\u00db\3\2\2\2\u041a\u041b\7G\2\2\u041b\u041d\7^\2\2\u041c") + buf.write("\u041e\5\u00e0q\2\u041d\u041c\3\2\2\2\u041d\u041e\3\2") + buf.write("\2\2\u041e\u0422\3\2\2\2\u041f\u0421\5\u00dep\2\u0420") + buf.write("\u041f\3\2\2\2\u0421\u0424\3\2\2\2\u0422\u0420\3\2\2\2") + buf.write("\u0422\u0423\3\2\2\2\u0423\u0425\3\2\2\2\u0424\u0422\3") + buf.write("\2\2\2\u0425\u0426\7_\2\2\u0426\u0427\7[\2\2\u0427\u00dd") + buf.write("\3\2\2\2\u0428\u0429\7\n\2\2\u0429\u042a\7B\2\2\u042a") + buf.write("\u042b\7\13\2\2\u042b\u043f\5\u00e2r\2\u042c\u042d\7\n") + buf.write("\2\2\u042d\u042e\7\b\2\2\u042e\u042f\7\13\2\2\u042f\u043f") + buf.write("\5\32\16\2\u0430\u0431\7\n\2\2\u0431\u0432\7H\2\2\u0432") + buf.write("\u0433\7\13\2\2\u0433\u043f\5\u0106\u0084\2\u0434\u0435") + buf.write("\7\n\2\2\u0435\u0436\7/\2\2\u0436\u0437\7\13\2\2\u0437") + buf.write("\u043f\5\u0102\u0082\2\u0438\u0439\7\n\2\2\u0439\u043a") + buf.write("\7(\2\2\u043a\u043b\7\13\2\2\u043b\u043f\5|?\2\u043c\u043d") + buf.write("\7\n\2\2\u043d\u043f\5\u0114\u008b\2\u043e\u0428\3\2\2") + buf.write("\2\u043e\u042c\3\2\2\2\u043e\u0430\3\2\2\2\u043e\u0434") + buf.write("\3\2\2\2\u043e\u0438\3\2\2\2\u043e\u043c\3\2\2\2\u043f") + buf.write("\u00df\3\2\2\2\u0440\u0441\7i\2\2\u0441\u00e1\3\2\2\2") + buf.write("\u0442\u0445\5\u00e6t\2\u0443\u0445\5\u00e4s\2\u0444\u0442") + buf.write("\3\2\2\2\u0444\u0443\3\2\2\2\u0445\u00e3\3\2\2\2\u0446") + buf.write("\u0447\5\u00e6t\2\u0447\u0448\7I\2\2\u0448\u0449\5\u00e2") + buf.write("r\2\u0449\u044a\7\13\2\2\u044a\u044b\5\u00e2r\2\u044b") + buf.write("\u00e5\3\2\2\2\u044c\u0451\5\u00e8u\2\u044d\u044e\7J\2") + buf.write("\2\u044e\u0450\5\u00e8u\2\u044f\u044d\3\2\2\2\u0450\u0453") + buf.write("\3\2\2\2\u0451\u044f\3\2\2\2\u0451\u0452\3\2\2\2\u0452") + buf.write("\u00e7\3\2\2\2\u0453\u0451\3\2\2\2\u0454\u0459\5\u00ea") + buf.write("v\2\u0455\u0456\7K\2\2\u0456\u0458\5\u00eav\2\u0457\u0455") + buf.write("\3\2\2\2\u0458\u045b\3\2\2\2\u0459\u0457\3\2\2\2\u0459") + buf.write("\u045a\3\2\2\2\u045a\u00e9\3\2\2\2\u045b\u0459\3\2\2\2") + buf.write("\u045c\u0461\5\u00ecw\2\u045d\u045e\7L\2\2\u045e\u0460") + buf.write("\5\u00ecw\2\u045f\u045d\3\2\2\2\u0460\u0463\3\2\2\2\u0461") + buf.write("\u045f\3\2\2\2\u0461\u0462\3\2\2\2\u0462\u00eb\3\2\2\2") + buf.write("\u0463\u0461\3\2\2\2\u0464\u0465\7M\2\2\u0465\u0468\5") + buf.write("\u00ecw\2\u0466\u0468\5\u00eex\2\u0467\u0464\3\2\2\2\u0467") + buf.write("\u0466\3\2\2\2\u0468\u00ed\3\2\2\2\u0469\u046a\bx\1\2") + buf.write("\u046a\u046b\5\u00f2z\2\u046b\u0472\3\2\2\2\u046c\u046d") + buf.write("\f\3\2\2\u046d\u046e\5\u00f0y\2\u046e\u046f\5\u00f2z\2") + buf.write("\u046f\u0471\3\2\2\2\u0470\u046c\3\2\2\2\u0471\u0474\3") + buf.write("\2\2\2\u0472\u0470\3\2\2\2\u0472\u0473\3\2\2\2\u0473\u00ef") + buf.write("\3\2\2\2\u0474\u0472\3\2\2\2\u0475\u0476\t\7\2\2\u0476") + buf.write("\u00f1\3\2\2\2\u0477\u0478\bz\1\2\u0478\u0479\5\u00f6") + buf.write("|\2\u0479\u0480\3\2\2\2\u047a\u047b\f\3\2\2\u047b\u047c") + buf.write("\5\u00f4{\2\u047c\u047d\5\u00f6|\2\u047d\u047f\3\2\2\2") + buf.write("\u047e\u047a\3\2\2\2\u047f\u0482\3\2\2\2\u0480\u047e\3") + buf.write("\2\2\2\u0480\u0481\3\2\2\2\u0481\u00f3\3\2\2\2\u0482\u0480") + buf.write("\3\2\2\2\u0483\u0484\t\b\2\2\u0484\u00f5\3\2\2\2\u0485") + buf.write("\u0486\b|\1\2\u0486\u0487\5\u00fa~\2\u0487\u048e\3\2\2") + buf.write("\2\u0488\u0489\f\3\2\2\u0489\u048a\5\u00f8}\2\u048a\u048b") + buf.write("\5\u00fa~\2\u048b\u048d\3\2\2\2\u048c\u0488\3\2\2\2\u048d") + buf.write("\u0490\3\2\2\2\u048e\u048c\3\2\2\2\u048e\u048f\3\2\2\2") + buf.write("\u048f\u00f7\3\2\2\2\u0490\u048e\3\2\2\2\u0491\u0492\t") + buf.write("\t\2\2\u0492\u00f9\3\2\2\2\u0493\u0497\5\u00fc\177\2\u0494") + buf.write("\u0495\7U\2\2\u0495\u0497\5\u00fa~\2\u0496\u0493\3\2\2") + buf.write("\2\u0496\u0494\3\2\2\2\u0497\u00fb\3\2\2\2\u0498\u0499") + buf.write("\b\177\1\2\u0499\u049a\5\u0100\u0081\2\u049a\u04b9\3\2") + buf.write("\2\2\u049b\u049c\f\7\2\2\u049c\u049d\7\4\2\2\u049d\u049e") + buf.write("\7+\2\2\u049e\u049f\7^\2\2\u049f\u04a0\5f\64\2\u04a0\u04a1") + buf.write("\7_\2\2\u04a1\u04b8\3\2\2\2\u04a2\u04a3\f\6\2\2\u04a3") + buf.write("\u04a4\7\4\2\2\u04a4\u04a5\7\6\2\2\u04a5\u04a6\7^\2\2") + buf.write("\u04a6\u04a7\5f\64\2\u04a7\u04a8\7_\2\2\u04a8\u04b8\3") + buf.write("\2\2\2\u04a9\u04aa\f\5\2\2\u04aa\u04ab\7\\\2\2\u04ab\u04ac") + buf.write("\5\u00e2r\2\u04ac\u04ad\7]\2\2\u04ad\u04b8\3\2\2\2\u04ae") + buf.write("\u04af\f\4\2\2\u04af\u04b1\7^\2\2\u04b0\u04b2\5\u0110") + buf.write("\u0089\2\u04b1\u04b0\3\2\2\2\u04b1\u04b2\3\2\2\2\u04b2") + buf.write("\u04b3\3\2\2\2\u04b3\u04b8\7_\2\2\u04b4\u04b5\f\3\2\2") + buf.write("\u04b5\u04b6\7\4\2\2\u04b6\u04b8\5<\37\2\u04b7\u049b\3") + buf.write("\2\2\2\u04b7\u04a2\3\2\2\2\u04b7\u04a9\3\2\2\2\u04b7\u04ae") + buf.write("\3\2\2\2\u04b7\u04b4\3\2\2\2\u04b8\u04bb\3\2\2\2\u04b9") + buf.write("\u04b7\3\2\2\2\u04b9\u04ba\3\2\2\2\u04ba\u00fd\3\2\2\2") + buf.write("\u04bb\u04b9\3\2\2\2\u04bc\u04bd\5\u00fc\177\2\u04bd\u04be") + buf.write("\7\4\2\2\u04be\u04bf\5<\37\2\u04bf\u00ff\3\2\2\2\u04c0") + buf.write("\u04c8\5\u0102\u0082\2\u04c1\u04c8\7Y\2\2\u04c2\u04c8") + buf.write("\7i\2\2\u04c3\u04c4\7^\2\2\u04c4\u04c5\5\u00e2r\2\u04c5") + buf.write("\u04c6\7_\2\2\u04c6\u04c8\3\2\2\2\u04c7\u04c0\3\2\2\2") + buf.write("\u04c7\u04c1\3\2\2\2\u04c7\u04c2\3\2\2\2\u04c7\u04c3\3") + buf.write("\2\2\2\u04c8\u0101\3\2\2\2\u04c9\u04d3\5\u0116\u008c\2") + buf.write("\u04ca\u04d3\7d\2\2\u04cb\u04d3\5\u0118\u008d\2\u04cc") + buf.write("\u04d3\7h\2\2\u04cd\u04d3\7c\2\2\u04ce\u04d3\5\u0108\u0085") + buf.write("\2\u04cf\u04d3\5\62\32\2\u04d0\u04d3\5\u0104\u0083\2\u04d1") + buf.write("\u04d3\5\u0106\u0084\2\u04d2\u04c9\3\2\2\2\u04d2\u04ca") + buf.write("\3\2\2\2\u04d2\u04cb\3\2\2\2\u04d2\u04cc\3\2\2\2\u04d2") + buf.write("\u04cd\3\2\2\2\u04d2\u04ce\3\2\2\2\u04d2\u04cf\3\2\2\2") + buf.write("\u04d2\u04d0\3\2\2\2\u04d2\u04d1\3\2\2\2\u04d3\u0103\3") + buf.write("\2\2\2\u04d4\u04d5\7\\\2\2\u04d5\u04da\5\u00e2r\2\u04d6") + buf.write("\u04d7\7\n\2\2\u04d7\u04d9\5\u00e2r\2\u04d8\u04d6\3\2") + buf.write("\2\2\u04d9\u04dc\3\2\2\2\u04da\u04d8\3\2\2\2\u04da\u04db") + buf.write("\3\2\2\2\u04db\u04dd\3\2\2\2\u04dc\u04da\3\2\2\2\u04dd") + buf.write("\u04de\7]\2\2\u04de\u0105\3\2\2\2\u04df\u04e0\7H\2\2\u04e0") + buf.write("\u04e1\7^\2\2\u04e1\u04e2\5\u00e2r\2\u04e2\u04e3\7\n\2") + buf.write("\2\u04e3\u04e4\5\u00e2r\2\u04e4\u04e5\7_\2\2\u04e5\u04ed") + buf.write("\3\2\2\2\u04e6\u04e7\7\\\2\2\u04e7\u04e8\5\u00e2r\2\u04e8") + buf.write("\u04e9\7Z\2\2\u04e9\u04ea\5\u00e2r\2\u04ea\u04eb\7]\2") + buf.write("\2\u04eb\u04ed\3\2\2\2\u04ec\u04df\3\2\2\2\u04ec\u04e6") + buf.write("\3\2\2\2\u04ed\u0107\3\2\2\2\u04ee\u04ef\5<\37\2\u04ef") + buf.write("\u04f0\7\4\2\2\u04f0\u04f2\3\2\2\2\u04f1\u04ee\3\2\2\2") + buf.write("\u04f2\u04f5\3\2\2\2\u04f3\u04f1\3\2\2\2\u04f3\u04f4\3") + buf.write("\2\2\2\u04f4\u04f6\3\2\2\2\u04f5\u04f3\3\2\2\2\u04f6\u04f7") + buf.write("\5<\37\2\u04f7\u0109\3\2\2\2\u04f8\u04fd\5\u010c\u0087") + buf.write("\2\u04f9\u04fa\7\n\2\2\u04fa\u04fc\5\u010c\u0087\2\u04fb") + buf.write("\u04f9\3\2\2\2\u04fc\u04ff\3\2\2\2\u04fd\u04fb\3\2\2\2") + buf.write("\u04fd\u04fe\3\2\2\2\u04fe\u010b\3\2\2\2\u04ff\u04fd\3") + buf.write("\2\2\2\u0500\u0501\5\u010e\u0088\2\u0501\u0502\7\13\2") + buf.write("\2\u0502\u0505\5f\64\2\u0503\u0504\7\27\2\2\u0504\u0506") + buf.write("\5\u0096L\2\u0505\u0503\3\2\2\2\u0505\u0506\3\2\2\2\u0506") + buf.write("\u010d\3\2\2\2\u0507\u0508\7i\2\2\u0508\u010f\3\2\2\2") + buf.write("\u0509\u050e\5\u0112\u008a\2\u050a\u050b\7\n\2\2\u050b") + buf.write("\u050d\5\u0112\u008a\2\u050c\u050a\3\2\2\2\u050d\u0510") + buf.write("\3\2\2\2\u050e\u050c\3\2\2\2\u050e\u050f\3\2\2\2\u050f") + buf.write("\u0515\3\2\2\2\u0510\u050e\3\2\2\2\u0511\u0512\7\n\2\2") + buf.write("\u0512\u0514\5\u0114\u008b\2\u0513\u0511\3\2\2\2\u0514") + buf.write("\u0517\3\2\2\2\u0515\u0513\3\2\2\2\u0515\u0516\3\2\2\2") + buf.write("\u0516\u0521\3\2\2\2\u0517\u0515\3\2\2\2\u0518\u051d\5") + buf.write("\u0114\u008b\2\u0519\u051a\7\n\2\2\u051a\u051c\5\u0114") + buf.write("\u008b\2\u051b\u0519\3\2\2\2\u051c\u051f\3\2\2\2\u051d") + buf.write("\u051b\3\2\2\2\u051d\u051e\3\2\2\2\u051e\u0521\3\2\2\2") + buf.write("\u051f\u051d\3\2\2\2\u0520\u0509\3\2\2\2\u0520\u0518\3") + buf.write("\2\2\2\u0521\u0111\3\2\2\2\u0522\u0523\5\u00e2r\2\u0523") + buf.write("\u0113\3\2\2\2\u0524\u0525\5\u010e\u0088\2\u0525\u0526") + buf.write("\7\13\2\2\u0526\u0527\5\u00e2r\2\u0527\u0115\3\2\2\2\u0528") + buf.write("\u052b\7d\2\2\u0529\u052b\5\u0118\u008d\2\u052a\u0528") + buf.write("\3\2\2\2\u052a\u0529\3\2\2\2\u052b\u052c\3\2\2\2\u052c") + buf.write("\u052d\5\32\16\2\u052d\u0117\3\2\2\2\u052e\u052f\t\n\2") + buf.write("\2\u052f\u0119\3\2\2\2\u0080\u011d\u0123\u012f\u0133\u013d") + buf.write("\u014b\u0168\u016f\u017b\u017f\u0187\u018d\u019a\u01a3") + buf.write("\u01b4\u01bb\u01c3\u01c8\u01cd\u01d4\u01dd\u01e5\u01ea") + buf.write("\u01ef\u01f6\u01fd\u0204\u0206\u020b\u0210\u0218\u021d") + buf.write("\u0226\u022d\u022f\u0234\u0239\u023f\u0244\u024c\u0251") + buf.write("\u0257\u0262\u0270\u0276\u027c\u0284\u028b\u028f\u0293") + buf.write("\u0298\u02aa\u02ae\u02b4\u02b8\u02bb\u02ca\u02d3\u02ec") + buf.write("\u02f6\u02fd\u0304\u0308\u0310\u0318\u031a\u0325\u0332") + buf.write("\u0338\u033c\u0341\u0353\u0357\u035b\u0360\u036c\u0376") + buf.write("\u037c\u0384\u038b\u0390\u0393\u039b\u039f\u03a6\u03ab") + buf.write("\u03b0\u03b9\u03c0\u03d0\u03df\u03e7\u03ec\u03f5\u03fe") + buf.write("\u0402\u040a\u040f\u0414\u041d\u0422\u043e\u0444\u0451") + buf.write("\u0459\u0461\u0467\u0472\u0480\u048e\u0496\u04b1\u04b7") + buf.write("\u04b9\u04c7\u04d2\u04da\u04ec\u04f3\u04fd\u0505\u050e") + buf.write("\u0515\u051d\u0520\u052a") + return buf.getvalue() + + +class OpenSCENARIO2Parser (Parser): + + grammarFileName = "OpenSCENARIO2.g4" + + atn = ATNDeserializer().deserialize(serializedATN()) + + decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)] + + sharedContextCache = PredictionContextCache() + + literalNames = ["", "'import'", "'.'", "'type'", "'is'", "'SI'", + "'unit'", "'of'", "','", "':'", "'factor'", "'offset'", + "'kg'", "'m'", "'s'", "'A'", "'K'", "'mol'", "'cd'", + "'rad'", "'enum'", "'='", "'!'", "'=='", "'struct'", + "'inherits'", "'actor'", "'scenario'", "'action'", + "'modifier'", "'extend'", "'global'", "'list'", "'int'", + "'uint'", "'float'", "'bool'", "'string'", "'event'", + "'if'", "'@'", "'as'", "'rise'", "'fall'", "'elapsed'", + "'every'", "'var'", "'sample'", "'with'", "'keep'", + "'default'", "'hard'", "'remove_default'", "'on'", + "'do'", "'serial'", "'one_of'", "'parallel'", "'wait'", + "'emit'", "'call'", "'until'", "'def'", "'->'", "'expression'", + "'undefined'", "'external'", "'only'", "'cover'", "'record'", + "'range'", "'?'", "'=>'", "'or'", "'and'", "'not'", + "'!='", "'<'", "'<='", "'>'", "'>='", "'in'", "'+'", + "'-'", "'*'", "'/'", "'%'", "'it'", "'..'", "", + "'['", "']'", "'('", "')'"] + + symbolicNames = ["", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "", "", "", + "", "NEWLINE", "OPEN_BRACK", "CLOSE_BRACK", + "OPEN_PAREN", "CLOSE_PAREN", "SKIP_", "BLOCK_COMMENT", + "LINE_COMMENT", "StringLiteral", "FloatLiteral", "UintLiteral", + "HexUintLiteral", "IntLiteral", "BoolLiteral", "Identifier", + "INDENT", "DEDENT"] + + RULE_osc_file = 0 + RULE_preludeStatement = 1 + RULE_importStatement = 2 + RULE_importReference = 3 + RULE_structuredIdentifier = 4 + RULE_oscDeclaration = 5 + RULE_physicalTypeDeclaration = 6 + RULE_physicalTypeName = 7 + RULE_baseUnitSpecifier = 8 + RULE_sIBaseUnitSpecifier = 9 + RULE_unitDeclaration = 10 + RULE_unitSpecifier = 11 + RULE_unitName = 12 + RULE_siBaseExponentList = 13 + RULE_siBaseExponent = 14 + RULE_siUnitSpecifier = 15 + RULE_siFactor = 16 + RULE_siOffset = 17 + RULE_siBaseUnitName = 18 + RULE_enumDeclaration = 19 + RULE_enumMemberDecl = 20 + RULE_enumMemberValue = 21 + RULE_enumName = 22 + RULE_enumMemberName = 23 + RULE_enumValueReference = 24 + RULE_inheritsCondition = 25 + RULE_structDeclaration = 26 + RULE_structInherits = 27 + RULE_structMemberDecl = 28 + RULE_fieldName = 29 + RULE_structName = 30 + RULE_actorDeclaration = 31 + RULE_actorInherits = 32 + RULE_actorMemberDecl = 33 + RULE_actorName = 34 + RULE_scenarioDeclaration = 35 + RULE_scenarioInherits = 36 + RULE_scenarioMemberDecl = 37 + RULE_qualifiedBehaviorName = 38 + RULE_behaviorName = 39 + RULE_actionDeclaration = 40 + RULE_actionInherits = 41 + RULE_modifierDeclaration = 42 + RULE_modifierName = 43 + RULE_typeExtension = 44 + RULE_enumTypeExtension = 45 + RULE_structuredTypeExtension = 46 + RULE_extendableTypeName = 47 + RULE_extensionMemberDecl = 48 + RULE_globalParameterDeclaration = 49 + RULE_typeDeclarator = 50 + RULE_nonAggregateTypeDeclarator = 51 + RULE_aggregateTypeDeclarator = 52 + RULE_listTypeDeclarator = 53 + RULE_primitiveType = 54 + RULE_typeName = 55 + RULE_eventDeclaration = 56 + RULE_eventSpecification = 57 + RULE_eventReference = 58 + RULE_eventFieldDecl = 59 + RULE_eventFieldName = 60 + RULE_eventName = 61 + RULE_eventPath = 62 + RULE_eventCondition = 63 + RULE_riseExpression = 64 + RULE_fallExpression = 65 + RULE_elapsedExpression = 66 + RULE_everyExpression = 67 + RULE_boolExpression = 68 + RULE_durationExpression = 69 + RULE_fieldDeclaration = 70 + RULE_parameterDeclaration = 71 + RULE_variableDeclaration = 72 + RULE_sampleExpression = 73 + RULE_defaultValue = 74 + RULE_parameterWithDeclaration = 75 + RULE_parameterWithMember = 76 + RULE_constraintDeclaration = 77 + RULE_keepConstraintDeclaration = 78 + RULE_constraintQualifier = 79 + RULE_constraintExpression = 80 + RULE_removeDefaultDeclaration = 81 + RULE_parameterReference = 82 + RULE_modifierInvocation = 83 + RULE_behaviorExpression = 84 + RULE_behaviorSpecification = 85 + RULE_onDirective = 86 + RULE_onMember = 87 + RULE_doDirective = 88 + RULE_doMember = 89 + RULE_composition = 90 + RULE_compositionOperator = 91 + RULE_behaviorInvocation = 92 + RULE_behaviorWithDeclaration = 93 + RULE_behaviorWithMember = 94 + RULE_labelName = 95 + RULE_actorExpression = 96 + RULE_waitDirective = 97 + RULE_emitDirective = 98 + RULE_callDirective = 99 + RULE_untilDirective = 100 + RULE_methodInvocation = 101 + RULE_methodDeclaration = 102 + RULE_returnType = 103 + RULE_methodImplementation = 104 + RULE_methodQualifier = 105 + RULE_methodName = 106 + RULE_coverageDeclaration = 107 + RULE_coverDeclaration = 108 + RULE_recordDeclaration = 109 + RULE_coverageArgumentList = 110 + RULE_targetName = 111 + RULE_expression = 112 + RULE_ternaryOpExp = 113 + RULE_implication = 114 + RULE_disjunction = 115 + RULE_conjunction = 116 + RULE_inversion = 117 + RULE_relation = 118 + RULE_relationalOp = 119 + RULE_sumExpression = 120 + RULE_additiveOp = 121 + RULE_term = 122 + RULE_multiplicativeOp = 123 + RULE_factor = 124 + RULE_postfixExp = 125 + RULE_fieldAccess = 126 + RULE_primaryExp = 127 + RULE_valueExp = 128 + RULE_listConstructor = 129 + RULE_rangeConstructor = 130 + RULE_identifierReference = 131 + RULE_argumentListSpecification = 132 + RULE_argumentSpecification = 133 + RULE_argumentName = 134 + RULE_argumentList = 135 + RULE_positionalArgument = 136 + RULE_namedArgument = 137 + RULE_physicalLiteral = 138 + RULE_integerLiteral = 139 + + ruleNames = ["osc_file", "preludeStatement", "importStatement", "importReference", + "structuredIdentifier", "oscDeclaration", "physicalTypeDeclaration", + "physicalTypeName", "baseUnitSpecifier", "sIBaseUnitSpecifier", + "unitDeclaration", "unitSpecifier", "unitName", "siBaseExponentList", + "siBaseExponent", "siUnitSpecifier", "siFactor", "siOffset", + "siBaseUnitName", "enumDeclaration", "enumMemberDecl", + "enumMemberValue", "enumName", "enumMemberName", "enumValueReference", + "inheritsCondition", "structDeclaration", "structInherits", + "structMemberDecl", "fieldName", "structName", "actorDeclaration", + "actorInherits", "actorMemberDecl", "actorName", "scenarioDeclaration", + "scenarioInherits", "scenarioMemberDecl", "qualifiedBehaviorName", + "behaviorName", "actionDeclaration", "actionInherits", + "modifierDeclaration", "modifierName", "typeExtension", + "enumTypeExtension", "structuredTypeExtension", "extendableTypeName", + "extensionMemberDecl", "globalParameterDeclaration", + "typeDeclarator", "nonAggregateTypeDeclarator", "aggregateTypeDeclarator", + "listTypeDeclarator", "primitiveType", "typeName", "eventDeclaration", + "eventSpecification", "eventReference", "eventFieldDecl", + "eventFieldName", "eventName", "eventPath", "eventCondition", + "riseExpression", "fallExpression", "elapsedExpression", + "everyExpression", "boolExpression", "durationExpression", + "fieldDeclaration", "parameterDeclaration", "variableDeclaration", + "sampleExpression", "defaultValue", "parameterWithDeclaration", + "parameterWithMember", "constraintDeclaration", "keepConstraintDeclaration", + "constraintQualifier", "constraintExpression", "removeDefaultDeclaration", + "parameterReference", "modifierInvocation", "behaviorExpression", + "behaviorSpecification", "onDirective", "onMember", "doDirective", + "doMember", "composition", "compositionOperator", "behaviorInvocation", + "behaviorWithDeclaration", "behaviorWithMember", "labelName", + "actorExpression", "waitDirective", "emitDirective", + "callDirective", "untilDirective", "methodInvocation", + "methodDeclaration", "returnType", "methodImplementation", + "methodQualifier", "methodName", "coverageDeclaration", + "coverDeclaration", "recordDeclaration", "coverageArgumentList", + "targetName", "expression", "ternaryOpExp", "implication", + "disjunction", "conjunction", "inversion", "relation", + "relationalOp", "sumExpression", "additiveOp", "term", + "multiplicativeOp", "factor", "postfixExp", "fieldAccess", + "primaryExp", "valueExp", "listConstructor", "rangeConstructor", + "identifierReference", "argumentListSpecification", "argumentSpecification", + "argumentName", "argumentList", "positionalArgument", + "namedArgument", "physicalLiteral", "integerLiteral"] + + EOF = Token.EOF + T__0 = 1 + T__1 = 2 + T__2 = 3 + T__3 = 4 + T__4 = 5 + T__5 = 6 + T__6 = 7 + T__7 = 8 + T__8 = 9 + T__9 = 10 + T__10 = 11 + T__11 = 12 + T__12 = 13 + T__13 = 14 + T__14 = 15 + T__15 = 16 + T__16 = 17 + T__17 = 18 + T__18 = 19 + T__19 = 20 + T__20 = 21 + T__21 = 22 + T__22 = 23 + T__23 = 24 + T__24 = 25 + T__25 = 26 + T__26 = 27 + T__27 = 28 + T__28 = 29 + T__29 = 30 + T__30 = 31 + T__31 = 32 + T__32 = 33 + T__33 = 34 + T__34 = 35 + T__35 = 36 + T__36 = 37 + T__37 = 38 + T__38 = 39 + T__39 = 40 + T__40 = 41 + T__41 = 42 + T__42 = 43 + T__43 = 44 + T__44 = 45 + T__45 = 46 + T__46 = 47 + T__47 = 48 + T__48 = 49 + T__49 = 50 + T__50 = 51 + T__51 = 52 + T__52 = 53 + T__53 = 54 + T__54 = 55 + T__55 = 56 + T__56 = 57 + T__57 = 58 + T__58 = 59 + T__59 = 60 + T__60 = 61 + T__61 = 62 + T__62 = 63 + T__63 = 64 + T__64 = 65 + T__65 = 66 + T__66 = 67 + T__67 = 68 + T__68 = 69 + T__69 = 70 + T__70 = 71 + T__71 = 72 + T__72 = 73 + T__73 = 74 + T__74 = 75 + T__75 = 76 + T__76 = 77 + T__77 = 78 + T__78 = 79 + T__79 = 80 + T__80 = 81 + T__81 = 82 + T__82 = 83 + T__83 = 84 + T__84 = 85 + T__85 = 86 + T__86 = 87 + T__87 = 88 + NEWLINE = 89 + OPEN_BRACK = 90 + CLOSE_BRACK = 91 + OPEN_PAREN = 92 + CLOSE_PAREN = 93 + SKIP_ = 94 + BLOCK_COMMENT = 95 + LINE_COMMENT = 96 + StringLiteral = 97 + FloatLiteral = 98 + UintLiteral = 99 + HexUintLiteral = 100 + IntLiteral = 101 + BoolLiteral = 102 + Identifier = 103 + INDENT = 104 + DEDENT = 105 + + def __init__(self, input: TokenStream, output: TextIO = sys.stdout): + super().__init__(input, output) + self.checkVersion("4.7.2") + self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) + self._predicates = None + + class Osc_fileContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def EOF(self): + return self.getToken(OpenSCENARIO2Parser.EOF, 0) + + def preludeStatement(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.PreludeStatementContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.PreludeStatementContext, i) + + def oscDeclaration(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.OscDeclarationContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.OscDeclarationContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_osc_file + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterOsc_file"): + listener.enterOsc_file(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitOsc_file"): + listener.exitOsc_file(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitOsc_file"): + return visitor.visitOsc_file(self) + else: + return visitor.visitChildren(self) + + def osc_file(self): + + localctx = OpenSCENARIO2Parser.Osc_fileContext(self, self._ctx, self.state) + self.enterRule(localctx, 0, self.RULE_osc_file) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 283 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 0, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + self.state = 280 + self.preludeStatement() + self.state = 285 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 0, self._ctx) + + self.state = 289 + self._errHandler.sync(self) + _la = self._input.LA(1) + while (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__2) | (1 << OpenSCENARIO2Parser.T__5) | (1 << OpenSCENARIO2Parser.T__19) | (1 << OpenSCENARIO2Parser.T__23) | (1 << OpenSCENARIO2Parser.T__25) | (1 << OpenSCENARIO2Parser.T__26) | (1 << OpenSCENARIO2Parser.T__27) | (1 << OpenSCENARIO2Parser.T__28) | (1 << OpenSCENARIO2Parser.T__29) | (1 << OpenSCENARIO2Parser.T__30))) != 0) or _la == OpenSCENARIO2Parser.NEWLINE: + self.state = 286 + self.oscDeclaration() + self.state = 291 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 292 + self.match(OpenSCENARIO2Parser.EOF) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class PreludeStatementContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def importStatement(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ImportStatementContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_preludeStatement + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPreludeStatement"): + listener.enterPreludeStatement(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPreludeStatement"): + listener.exitPreludeStatement(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPreludeStatement"): + return visitor.visitPreludeStatement(self) + else: + return visitor.visitChildren(self) + + def preludeStatement(self): + + localctx = OpenSCENARIO2Parser.PreludeStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 2, self.RULE_preludeStatement) + try: + self.enterOuterAlt(localctx, 1) + self.state = 294 + self.importStatement() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ImportStatementContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def importReference(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ImportReferenceContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_importStatement + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterImportStatement"): + listener.enterImportStatement(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitImportStatement"): + listener.exitImportStatement(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitImportStatement"): + return visitor.visitImportStatement(self) + else: + return visitor.visitChildren(self) + + def importStatement(self): + + localctx = OpenSCENARIO2Parser.ImportStatementContext(self, self._ctx, self.state) + self.enterRule(localctx, 4, self.RULE_importStatement) + try: + self.state = 301 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__0]: + self.enterOuterAlt(localctx, 1) + self.state = 296 + self.match(OpenSCENARIO2Parser.T__0) + self.state = 297 + self.importReference() + self.state = 298 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.enterOuterAlt(localctx, 2) + self.state = 300 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ImportReferenceContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def StringLiteral(self): + return self.getToken(OpenSCENARIO2Parser.StringLiteral, 0) + + def structuredIdentifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructuredIdentifierContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_importReference + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterImportReference"): + listener.enterImportReference(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitImportReference"): + listener.exitImportReference(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitImportReference"): + return visitor.visitImportReference(self) + else: + return visitor.visitChildren(self) + + def importReference(self): + + localctx = OpenSCENARIO2Parser.ImportReferenceContext(self, self._ctx, self.state) + self.enterRule(localctx, 6, self.RULE_importReference) + try: + self.state = 305 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.StringLiteral]: + self.enterOuterAlt(localctx, 1) + self.state = 303 + self.match(OpenSCENARIO2Parser.StringLiteral) + pass + elif token in [OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 2) + self.state = 304 + self.structuredIdentifier(0) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class StructuredIdentifierContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def structuredIdentifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructuredIdentifierContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_structuredIdentifier + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStructuredIdentifier"): + listener.enterStructuredIdentifier(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStructuredIdentifier"): + listener.exitStructuredIdentifier(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStructuredIdentifier"): + return visitor.visitStructuredIdentifier(self) + else: + return visitor.visitChildren(self) + + def structuredIdentifier(self, _p: int = 0): + _parentctx = self._ctx + _parentState = self.state + localctx = OpenSCENARIO2Parser.StructuredIdentifierContext(self, self._ctx, _parentState) + _prevctx = localctx + _startState = 8 + self.enterRecursionRule(localctx, 8, self.RULE_structuredIdentifier, _p) + try: + self.enterOuterAlt(localctx, 1) + self.state = 308 + self.match(OpenSCENARIO2Parser.Identifier) + self._ctx.stop = self._input.LT(-1) + self.state = 315 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 4, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + if self._parseListeners is not None: + self.triggerExitRuleEvent() + _prevctx = localctx + localctx = OpenSCENARIO2Parser.StructuredIdentifierContext(self, _parentctx, _parentState) + self.pushNewRecursionContext(localctx, _startState, self.RULE_structuredIdentifier) + self.state = 310 + if not self.precpred(self._ctx, 1): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 1)") + self.state = 311 + self.match(OpenSCENARIO2Parser.T__1) + self.state = 312 + self.match(OpenSCENARIO2Parser.Identifier) + self.state = 317 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 4, self._ctx) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.unrollRecursionContexts(_parentctx) + return localctx + + class OscDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def physicalTypeDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PhysicalTypeDeclarationContext, 0) + + def unitDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.UnitDeclarationContext, 0) + + def enumDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumDeclarationContext, 0) + + def structDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructDeclarationContext, 0) + + def actorDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorDeclarationContext, 0) + + def actionDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActionDeclarationContext, 0) + + def scenarioDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ScenarioDeclarationContext, 0) + + def modifierDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ModifierDeclarationContext, 0) + + def typeExtension(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeExtensionContext, 0) + + def globalParameterDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.GlobalParameterDeclarationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_oscDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterOscDeclaration"): + listener.enterOscDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitOscDeclaration"): + listener.exitOscDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitOscDeclaration"): + return visitor.visitOscDeclaration(self) + else: + return visitor.visitChildren(self) + + def oscDeclaration(self): + + localctx = OpenSCENARIO2Parser.OscDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 10, self.RULE_oscDeclaration) + try: + self.state = 329 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__2]: + self.enterOuterAlt(localctx, 1) + self.state = 318 + self.physicalTypeDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__5]: + self.enterOuterAlt(localctx, 2) + self.state = 319 + self.unitDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__19]: + self.enterOuterAlt(localctx, 3) + self.state = 320 + self.enumDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__23]: + self.enterOuterAlt(localctx, 4) + self.state = 321 + self.structDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__25]: + self.enterOuterAlt(localctx, 5) + self.state = 322 + self.actorDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__27]: + self.enterOuterAlt(localctx, 6) + self.state = 323 + self.actionDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__26]: + self.enterOuterAlt(localctx, 7) + self.state = 324 + self.scenarioDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__28]: + self.enterOuterAlt(localctx, 8) + self.state = 325 + self.modifierDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__29]: + self.enterOuterAlt(localctx, 9) + self.state = 326 + self.typeExtension() + pass + elif token in [OpenSCENARIO2Parser.T__30]: + self.enterOuterAlt(localctx, 10) + self.state = 327 + self.globalParameterDeclaration() + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.enterOuterAlt(localctx, 11) + self.state = 328 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class PhysicalTypeDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def physicalTypeName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PhysicalTypeNameContext, 0) + + def baseUnitSpecifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BaseUnitSpecifierContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_physicalTypeDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPhysicalTypeDeclaration"): + listener.enterPhysicalTypeDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPhysicalTypeDeclaration"): + listener.exitPhysicalTypeDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPhysicalTypeDeclaration"): + return visitor.visitPhysicalTypeDeclaration(self) + else: + return visitor.visitChildren(self) + + def physicalTypeDeclaration(self): + + localctx = OpenSCENARIO2Parser.PhysicalTypeDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 12, self.RULE_physicalTypeDeclaration) + try: + self.enterOuterAlt(localctx, 1) + self.state = 331 + self.match(OpenSCENARIO2Parser.T__2) + self.state = 332 + self.physicalTypeName() + self.state = 333 + self.match(OpenSCENARIO2Parser.T__3) + self.state = 334 + self.baseUnitSpecifier() + self.state = 335 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class PhysicalTypeNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_physicalTypeName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPhysicalTypeName"): + listener.enterPhysicalTypeName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPhysicalTypeName"): + listener.exitPhysicalTypeName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPhysicalTypeName"): + return visitor.visitPhysicalTypeName(self) + else: + return visitor.visitChildren(self) + + def physicalTypeName(self): + + localctx = OpenSCENARIO2Parser.PhysicalTypeNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 14, self.RULE_physicalTypeName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 337 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class BaseUnitSpecifierContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def sIBaseUnitSpecifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SIBaseUnitSpecifierContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_baseUnitSpecifier + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBaseUnitSpecifier"): + listener.enterBaseUnitSpecifier(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBaseUnitSpecifier"): + listener.exitBaseUnitSpecifier(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBaseUnitSpecifier"): + return visitor.visitBaseUnitSpecifier(self) + else: + return visitor.visitChildren(self) + + def baseUnitSpecifier(self): + + localctx = OpenSCENARIO2Parser.BaseUnitSpecifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 16, self.RULE_baseUnitSpecifier) + try: + self.enterOuterAlt(localctx, 1) + self.state = 339 + self.sIBaseUnitSpecifier() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SIBaseUnitSpecifierContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def siBaseExponentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SiBaseExponentListContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_sIBaseUnitSpecifier + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSIBaseUnitSpecifier"): + listener.enterSIBaseUnitSpecifier(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSIBaseUnitSpecifier"): + listener.exitSIBaseUnitSpecifier(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSIBaseUnitSpecifier"): + return visitor.visitSIBaseUnitSpecifier(self) + else: + return visitor.visitChildren(self) + + def sIBaseUnitSpecifier(self): + + localctx = OpenSCENARIO2Parser.SIBaseUnitSpecifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 18, self.RULE_sIBaseUnitSpecifier) + try: + self.enterOuterAlt(localctx, 1) + self.state = 341 + self.match(OpenSCENARIO2Parser.T__4) + self.state = 342 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 343 + self.siBaseExponentList() + self.state = 344 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class UnitDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def unitName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.UnitNameContext, 0) + + def physicalTypeName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PhysicalTypeNameContext, 0) + + def unitSpecifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.UnitSpecifierContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_unitDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterUnitDeclaration"): + listener.enterUnitDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitUnitDeclaration"): + listener.exitUnitDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitUnitDeclaration"): + return visitor.visitUnitDeclaration(self) + else: + return visitor.visitChildren(self) + + def unitDeclaration(self): + + localctx = OpenSCENARIO2Parser.UnitDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 20, self.RULE_unitDeclaration) + try: + self.enterOuterAlt(localctx, 1) + self.state = 346 + self.match(OpenSCENARIO2Parser.T__5) + self.state = 347 + self.unitName() + self.state = 348 + self.match(OpenSCENARIO2Parser.T__6) + self.state = 349 + self.physicalTypeName() + self.state = 350 + self.match(OpenSCENARIO2Parser.T__3) + self.state = 351 + self.unitSpecifier() + self.state = 352 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class UnitSpecifierContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def siUnitSpecifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SiUnitSpecifierContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_unitSpecifier + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterUnitSpecifier"): + listener.enterUnitSpecifier(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitUnitSpecifier"): + listener.exitUnitSpecifier(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitUnitSpecifier"): + return visitor.visitUnitSpecifier(self) + else: + return visitor.visitChildren(self) + + def unitSpecifier(self): + + localctx = OpenSCENARIO2Parser.UnitSpecifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 22, self.RULE_unitSpecifier) + try: + self.enterOuterAlt(localctx, 1) + self.state = 354 + self.siUnitSpecifier() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class UnitNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def siBaseUnitName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SiBaseUnitNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_unitName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterUnitName"): + listener.enterUnitName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitUnitName"): + listener.exitUnitName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitUnitName"): + return visitor.visitUnitName(self) + else: + return visitor.visitChildren(self) + + def unitName(self): + + localctx = OpenSCENARIO2Parser.UnitNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 24, self.RULE_unitName) + try: + self.state = 358 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 1) + self.state = 356 + self.match(OpenSCENARIO2Parser.Identifier) + pass + elif token in [OpenSCENARIO2Parser.T__11, OpenSCENARIO2Parser.T__12, OpenSCENARIO2Parser.T__13, OpenSCENARIO2Parser.T__14, OpenSCENARIO2Parser.T__15, OpenSCENARIO2Parser.T__16, OpenSCENARIO2Parser.T__17, OpenSCENARIO2Parser.T__18]: + self.enterOuterAlt(localctx, 2) + self.state = 357 + self.siBaseUnitName() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SiBaseExponentListContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def siBaseExponent(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.SiBaseExponentContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.SiBaseExponentContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_siBaseExponentList + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSiBaseExponentList"): + listener.enterSiBaseExponentList(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSiBaseExponentList"): + listener.exitSiBaseExponentList(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSiBaseExponentList"): + return visitor.visitSiBaseExponentList(self) + else: + return visitor.visitChildren(self) + + def siBaseExponentList(self): + + localctx = OpenSCENARIO2Parser.SiBaseExponentListContext(self, self._ctx, self.state) + self.enterRule(localctx, 26, self.RULE_siBaseExponentList) + try: + self.enterOuterAlt(localctx, 1) + self.state = 360 + self.siBaseExponent() + self.state = 365 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 7, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + self.state = 361 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 362 + self.siBaseExponent() + self.state = 367 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 7, self._ctx) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SiBaseExponentContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def siBaseUnitName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SiBaseUnitNameContext, 0) + + def integerLiteral(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.IntegerLiteralContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_siBaseExponent + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSiBaseExponent"): + listener.enterSiBaseExponent(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSiBaseExponent"): + listener.exitSiBaseExponent(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSiBaseExponent"): + return visitor.visitSiBaseExponent(self) + else: + return visitor.visitChildren(self) + + def siBaseExponent(self): + + localctx = OpenSCENARIO2Parser.SiBaseExponentContext(self, self._ctx, self.state) + self.enterRule(localctx, 28, self.RULE_siBaseExponent) + try: + self.enterOuterAlt(localctx, 1) + self.state = 368 + self.siBaseUnitName() + self.state = 369 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 370 + self.integerLiteral() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SiUnitSpecifierContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def siBaseExponentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SiBaseExponentListContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def siFactor(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SiFactorContext, 0) + + def siOffset(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SiOffsetContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_siUnitSpecifier + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSiUnitSpecifier"): + listener.enterSiUnitSpecifier(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSiUnitSpecifier"): + listener.exitSiUnitSpecifier(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSiUnitSpecifier"): + return visitor.visitSiUnitSpecifier(self) + else: + return visitor.visitChildren(self) + + def siUnitSpecifier(self): + + localctx = OpenSCENARIO2Parser.SiUnitSpecifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 30, self.RULE_siUnitSpecifier) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 372 + self.match(OpenSCENARIO2Parser.T__4) + self.state = 373 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 374 + self.siBaseExponentList() + self.state = 377 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 8, self._ctx) + if la_ == 1: + self.state = 375 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 376 + self.siFactor() + + self.state = 381 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__7: + self.state = 379 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 380 + self.siOffset() + + self.state = 383 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SiFactorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def FloatLiteral(self): + return self.getToken(OpenSCENARIO2Parser.FloatLiteral, 0) + + def integerLiteral(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.IntegerLiteralContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_siFactor + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSiFactor"): + listener.enterSiFactor(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSiFactor"): + listener.exitSiFactor(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSiFactor"): + return visitor.visitSiFactor(self) + else: + return visitor.visitChildren(self) + + def siFactor(self): + + localctx = OpenSCENARIO2Parser.SiFactorContext(self, self._ctx, self.state) + self.enterRule(localctx, 32, self.RULE_siFactor) + try: + self.enterOuterAlt(localctx, 1) + self.state = 385 + self.match(OpenSCENARIO2Parser.T__9) + self.state = 386 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 389 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.FloatLiteral]: + self.state = 387 + self.match(OpenSCENARIO2Parser.FloatLiteral) + pass + elif token in [OpenSCENARIO2Parser.UintLiteral, OpenSCENARIO2Parser.HexUintLiteral, OpenSCENARIO2Parser.IntLiteral]: + self.state = 388 + self.integerLiteral() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SiOffsetContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def FloatLiteral(self): + return self.getToken(OpenSCENARIO2Parser.FloatLiteral, 0) + + def integerLiteral(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.IntegerLiteralContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_siOffset + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSiOffset"): + listener.enterSiOffset(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSiOffset"): + listener.exitSiOffset(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSiOffset"): + return visitor.visitSiOffset(self) + else: + return visitor.visitChildren(self) + + def siOffset(self): + + localctx = OpenSCENARIO2Parser.SiOffsetContext(self, self._ctx, self.state) + self.enterRule(localctx, 34, self.RULE_siOffset) + try: + self.enterOuterAlt(localctx, 1) + self.state = 391 + self.match(OpenSCENARIO2Parser.T__10) + self.state = 392 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 395 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.FloatLiteral]: + self.state = 393 + self.match(OpenSCENARIO2Parser.FloatLiteral) + pass + elif token in [OpenSCENARIO2Parser.UintLiteral, OpenSCENARIO2Parser.HexUintLiteral, OpenSCENARIO2Parser.IntLiteral]: + self.state = 394 + self.integerLiteral() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SiBaseUnitNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_siBaseUnitName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSiBaseUnitName"): + listener.enterSiBaseUnitName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSiBaseUnitName"): + listener.exitSiBaseUnitName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSiBaseUnitName"): + return visitor.visitSiBaseUnitName(self) + else: + return visitor.visitChildren(self) + + def siBaseUnitName(self): + + localctx = OpenSCENARIO2Parser.SiBaseUnitNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 36, self.RULE_siBaseUnitName) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 397 + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__11) | (1 << OpenSCENARIO2Parser.T__12) | (1 << OpenSCENARIO2Parser.T__13) | (1 << OpenSCENARIO2Parser.T__14) | (1 << OpenSCENARIO2Parser.T__15) | (1 << OpenSCENARIO2Parser.T__16) | (1 << OpenSCENARIO2Parser.T__17) | (1 << OpenSCENARIO2Parser.T__18))) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EnumDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def enumName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumNameContext, 0) + + def OPEN_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_BRACK, 0) + + def enumMemberDecl(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.EnumMemberDeclContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumMemberDeclContext, i) + + def CLOSE_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_BRACK, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_enumDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEnumDeclaration"): + listener.enterEnumDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEnumDeclaration"): + listener.exitEnumDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEnumDeclaration"): + return visitor.visitEnumDeclaration(self) + else: + return visitor.visitChildren(self) + + def enumDeclaration(self): + + localctx = OpenSCENARIO2Parser.EnumDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 38, self.RULE_enumDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 399 + self.match(OpenSCENARIO2Parser.T__19) + self.state = 400 + self.enumName() + self.state = 401 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 402 + self.match(OpenSCENARIO2Parser.OPEN_BRACK) + self.state = 403 + self.enumMemberDecl() + self.state = 408 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 404 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 405 + self.enumMemberDecl() + self.state = 410 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 411 + self.match(OpenSCENARIO2Parser.CLOSE_BRACK) + self.state = 412 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EnumMemberDeclContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def enumMemberName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumMemberNameContext, 0) + + def enumMemberValue(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumMemberValueContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_enumMemberDecl + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEnumMemberDecl"): + listener.enterEnumMemberDecl(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEnumMemberDecl"): + listener.exitEnumMemberDecl(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEnumMemberDecl"): + return visitor.visitEnumMemberDecl(self) + else: + return visitor.visitChildren(self) + + def enumMemberDecl(self): + + localctx = OpenSCENARIO2Parser.EnumMemberDeclContext(self, self._ctx, self.state) + self.enterRule(localctx, 40, self.RULE_enumMemberDecl) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 414 + self.enumMemberName() + self.state = 417 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__20: + self.state = 415 + self.match(OpenSCENARIO2Parser.T__20) + self.state = 416 + self.enumMemberValue() + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EnumMemberValueContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def UintLiteral(self): + return self.getToken(OpenSCENARIO2Parser.UintLiteral, 0) + + def HexUintLiteral(self): + return self.getToken(OpenSCENARIO2Parser.HexUintLiteral, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_enumMemberValue + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEnumMemberValue"): + listener.enterEnumMemberValue(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEnumMemberValue"): + listener.exitEnumMemberValue(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEnumMemberValue"): + return visitor.visitEnumMemberValue(self) + else: + return visitor.visitChildren(self) + + def enumMemberValue(self): + + localctx = OpenSCENARIO2Parser.EnumMemberValueContext(self, self._ctx, self.state) + self.enterRule(localctx, 42, self.RULE_enumMemberValue) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 419 + _la = self._input.LA(1) + if not (_la == OpenSCENARIO2Parser.UintLiteral or _la == OpenSCENARIO2Parser.HexUintLiteral): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EnumNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_enumName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEnumName"): + listener.enterEnumName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEnumName"): + listener.exitEnumName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEnumName"): + return visitor.visitEnumName(self) + else: + return visitor.visitChildren(self) + + def enumName(self): + + localctx = OpenSCENARIO2Parser.EnumNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 44, self.RULE_enumName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 421 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EnumMemberNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_enumMemberName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEnumMemberName"): + listener.enterEnumMemberName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEnumMemberName"): + listener.exitEnumMemberName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEnumMemberName"): + return visitor.visitEnumMemberName(self) + else: + return visitor.visitChildren(self) + + def enumMemberName(self): + + localctx = OpenSCENARIO2Parser.EnumMemberNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 46, self.RULE_enumMemberName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 423 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EnumValueReferenceContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def enumName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumNameContext, 0) + + def enumMemberName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumMemberNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_enumValueReference + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEnumValueReference"): + listener.enterEnumValueReference(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEnumValueReference"): + listener.exitEnumValueReference(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEnumValueReference"): + return visitor.visitEnumValueReference(self) + else: + return visitor.visitChildren(self) + + def enumValueReference(self): + + localctx = OpenSCENARIO2Parser.EnumValueReferenceContext(self, self._ctx, self.state) + self.enterRule(localctx, 48, self.RULE_enumValueReference) + try: + self.enterOuterAlt(localctx, 1) + self.state = 425 + self.enumName() + self.state = 426 + self.match(OpenSCENARIO2Parser.T__21) + self.state = 427 + self.enumMemberName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class InheritsConditionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def fieldName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldNameContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def enumValueReference(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumValueReferenceContext, 0) + + def BoolLiteral(self): + return self.getToken(OpenSCENARIO2Parser.BoolLiteral, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_inheritsCondition + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterInheritsCondition"): + listener.enterInheritsCondition(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitInheritsCondition"): + listener.exitInheritsCondition(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitInheritsCondition"): + return visitor.visitInheritsCondition(self) + else: + return visitor.visitChildren(self) + + def inheritsCondition(self): + + localctx = OpenSCENARIO2Parser.InheritsConditionContext(self, self._ctx, self.state) + self.enterRule(localctx, 50, self.RULE_inheritsCondition) + try: + self.enterOuterAlt(localctx, 1) + self.state = 429 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 430 + self.fieldName() + self.state = 431 + self.match(OpenSCENARIO2Parser.T__22) + self.state = 434 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.Identifier]: + self.state = 432 + self.enumValueReference() + pass + elif token in [OpenSCENARIO2Parser.BoolLiteral]: + self.state = 433 + self.match(OpenSCENARIO2Parser.BoolLiteral) + pass + else: + raise NoViableAltException(self) + + self.state = 436 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class StructDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def structName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructNameContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def structInherits(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructInheritsContext, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def structMemberDecl(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.StructMemberDeclContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructMemberDeclContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_structDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStructDeclaration"): + listener.enterStructDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStructDeclaration"): + listener.exitStructDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStructDeclaration"): + return visitor.visitStructDeclaration(self) + else: + return visitor.visitChildren(self) + + def structDeclaration(self): + + localctx = OpenSCENARIO2Parser.StructDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 52, self.RULE_structDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 438 + self.match(OpenSCENARIO2Parser.T__23) + self.state = 439 + self.structName() + self.state = 441 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__24: + self.state = 440 + self.structInherits() + + self.state = 454 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__8]: + self.state = 443 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 444 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 445 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 447 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 446 + self.structMemberDecl() + self.state = 449 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__37) | (1 << OpenSCENARIO2Parser.T__45) | (1 << OpenSCENARIO2Parser.T__48) | (1 << OpenSCENARIO2Parser.T__51) | (1 << OpenSCENARIO2Parser.T__61))) != 0) or ((((_la - 68)) & ~0x3f) == 0 and ((1 << (_la - 68)) & ((1 << (OpenSCENARIO2Parser.T__67 - 68)) | (1 << (OpenSCENARIO2Parser.T__68 - 68)) | (1 << (OpenSCENARIO2Parser.Identifier - 68)))) != 0)): + break + + self.state = 451 + self.match(OpenSCENARIO2Parser.DEDENT) + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.state = 453 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class StructInheritsContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def structName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructNameContext, 0) + + def inheritsCondition(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.InheritsConditionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_structInherits + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStructInherits"): + listener.enterStructInherits(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStructInherits"): + listener.exitStructInherits(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStructInherits"): + return visitor.visitStructInherits(self) + else: + return visitor.visitChildren(self) + + def structInherits(self): + + localctx = OpenSCENARIO2Parser.StructInheritsContext(self, self._ctx, self.state) + self.enterRule(localctx, 54, self.RULE_structInherits) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 456 + self.match(OpenSCENARIO2Parser.T__24) + self.state = 457 + self.structName() + self.state = 459 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.OPEN_PAREN: + self.state = 458 + self.inheritsCondition() + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class StructMemberDeclContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventDeclarationContext, 0) + + def fieldDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldDeclarationContext, 0) + + def constraintDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ConstraintDeclarationContext, 0) + + def methodDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.MethodDeclarationContext, 0) + + def coverageDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CoverageDeclarationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_structMemberDecl + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStructMemberDecl"): + listener.enterStructMemberDecl(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStructMemberDecl"): + listener.exitStructMemberDecl(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStructMemberDecl"): + return visitor.visitStructMemberDecl(self) + else: + return visitor.visitChildren(self) + + def structMemberDecl(self): + + localctx = OpenSCENARIO2Parser.StructMemberDeclContext(self, self._ctx, self.state) + self.enterRule(localctx, 56, self.RULE_structMemberDecl) + try: + self.state = 466 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__37]: + self.enterOuterAlt(localctx, 1) + self.state = 461 + self.eventDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__45, OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 2) + self.state = 462 + self.fieldDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__48, OpenSCENARIO2Parser.T__51]: + self.enterOuterAlt(localctx, 3) + self.state = 463 + self.constraintDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__61]: + self.enterOuterAlt(localctx, 4) + self.state = 464 + self.methodDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__67, OpenSCENARIO2Parser.T__68]: + self.enterOuterAlt(localctx, 5) + self.state = 465 + self.coverageDeclaration() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class FieldNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_fieldName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFieldName"): + listener.enterFieldName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFieldName"): + listener.exitFieldName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFieldName"): + return visitor.visitFieldName(self) + else: + return visitor.visitChildren(self) + + def fieldName(self): + + localctx = OpenSCENARIO2Parser.FieldNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 58, self.RULE_fieldName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 468 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class StructNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_structName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStructName"): + listener.enterStructName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStructName"): + listener.exitStructName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStructName"): + return visitor.visitStructName(self) + else: + return visitor.visitChildren(self) + + def structName(self): + + localctx = OpenSCENARIO2Parser.StructNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 60, self.RULE_structName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 470 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ActorDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def actorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorNameContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def actorInherits(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorInheritsContext, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def actorMemberDecl(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ActorMemberDeclContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorMemberDeclContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_actorDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterActorDeclaration"): + listener.enterActorDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitActorDeclaration"): + listener.exitActorDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitActorDeclaration"): + return visitor.visitActorDeclaration(self) + else: + return visitor.visitChildren(self) + + def actorDeclaration(self): + + localctx = OpenSCENARIO2Parser.ActorDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 62, self.RULE_actorDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 472 + self.match(OpenSCENARIO2Parser.T__25) + self.state = 473 + self.actorName() + self.state = 475 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__24: + self.state = 474 + self.actorInherits() + + self.state = 488 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__8]: + self.state = 477 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 478 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 479 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 481 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 480 + self.actorMemberDecl() + self.state = 483 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__37) | (1 << OpenSCENARIO2Parser.T__45) | (1 << OpenSCENARIO2Parser.T__48) | (1 << OpenSCENARIO2Parser.T__51) | (1 << OpenSCENARIO2Parser.T__61))) != 0) or ((((_la - 68)) & ~0x3f) == 0 and ((1 << (_la - 68)) & ((1 << (OpenSCENARIO2Parser.T__67 - 68)) | (1 << (OpenSCENARIO2Parser.T__68 - 68)) | (1 << (OpenSCENARIO2Parser.Identifier - 68)))) != 0)): + break + + self.state = 485 + self.match(OpenSCENARIO2Parser.DEDENT) + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.state = 487 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ActorInheritsContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def actorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorNameContext, 0) + + def inheritsCondition(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.InheritsConditionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_actorInherits + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterActorInherits"): + listener.enterActorInherits(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitActorInherits"): + listener.exitActorInherits(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitActorInherits"): + return visitor.visitActorInherits(self) + else: + return visitor.visitChildren(self) + + def actorInherits(self): + + localctx = OpenSCENARIO2Parser.ActorInheritsContext(self, self._ctx, self.state) + self.enterRule(localctx, 64, self.RULE_actorInherits) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 490 + self.match(OpenSCENARIO2Parser.T__24) + self.state = 491 + self.actorName() + self.state = 493 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.OPEN_PAREN: + self.state = 492 + self.inheritsCondition() + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ActorMemberDeclContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventDeclarationContext, 0) + + def fieldDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldDeclarationContext, 0) + + def constraintDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ConstraintDeclarationContext, 0) + + def methodDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.MethodDeclarationContext, 0) + + def coverageDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CoverageDeclarationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_actorMemberDecl + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterActorMemberDecl"): + listener.enterActorMemberDecl(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitActorMemberDecl"): + listener.exitActorMemberDecl(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitActorMemberDecl"): + return visitor.visitActorMemberDecl(self) + else: + return visitor.visitChildren(self) + + def actorMemberDecl(self): + + localctx = OpenSCENARIO2Parser.ActorMemberDeclContext(self, self._ctx, self.state) + self.enterRule(localctx, 66, self.RULE_actorMemberDecl) + try: + self.state = 500 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__37]: + self.enterOuterAlt(localctx, 1) + self.state = 495 + self.eventDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__45, OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 2) + self.state = 496 + self.fieldDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__48, OpenSCENARIO2Parser.T__51]: + self.enterOuterAlt(localctx, 3) + self.state = 497 + self.constraintDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__61]: + self.enterOuterAlt(localctx, 4) + self.state = 498 + self.methodDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__67, OpenSCENARIO2Parser.T__68]: + self.enterOuterAlt(localctx, 5) + self.state = 499 + self.coverageDeclaration() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ActorNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_actorName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterActorName"): + listener.enterActorName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitActorName"): + listener.exitActorName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitActorName"): + return visitor.visitActorName(self) + else: + return visitor.visitChildren(self) + + def actorName(self): + + localctx = OpenSCENARIO2Parser.ActorNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 68, self.RULE_actorName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 502 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ScenarioDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def qualifiedBehaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.QualifiedBehaviorNameContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def scenarioInherits(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ScenarioInheritsContext, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def scenarioMemberDecl(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ScenarioMemberDeclContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ScenarioMemberDeclContext, i) + + def behaviorSpecification(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.BehaviorSpecificationContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorSpecificationContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_scenarioDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterScenarioDeclaration"): + listener.enterScenarioDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitScenarioDeclaration"): + listener.exitScenarioDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitScenarioDeclaration"): + return visitor.visitScenarioDeclaration(self) + else: + return visitor.visitChildren(self) + + def scenarioDeclaration(self): + + localctx = OpenSCENARIO2Parser.ScenarioDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 70, self.RULE_scenarioDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 504 + self.match(OpenSCENARIO2Parser.T__26) + self.state = 505 + self.qualifiedBehaviorName() + self.state = 507 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__24: + self.state = 506 + self.scenarioInherits() + + self.state = 521 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__8]: + self.state = 509 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 510 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 511 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 514 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 514 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__37, OpenSCENARIO2Parser.T__45, OpenSCENARIO2Parser.T__48, OpenSCENARIO2Parser.T__51, OpenSCENARIO2Parser.T__61, OpenSCENARIO2Parser.T__67, OpenSCENARIO2Parser.T__68, OpenSCENARIO2Parser.Identifier]: + self.state = 512 + self.scenarioMemberDecl() + pass + elif token in [OpenSCENARIO2Parser.T__52, OpenSCENARIO2Parser.T__53]: + self.state = 513 + self.behaviorSpecification() + pass + else: + raise NoViableAltException(self) + + self.state = 516 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__37) | (1 << OpenSCENARIO2Parser.T__45) | (1 << OpenSCENARIO2Parser.T__48) | (1 << OpenSCENARIO2Parser.T__51) | (1 << OpenSCENARIO2Parser.T__52) | (1 << OpenSCENARIO2Parser.T__53) | (1 << OpenSCENARIO2Parser.T__61))) != 0) or ((((_la - 68)) & ~0x3f) == 0 and ((1 << (_la - 68)) & ((1 << (OpenSCENARIO2Parser.T__67 - 68)) | (1 << (OpenSCENARIO2Parser.T__68 - 68)) | (1 << (OpenSCENARIO2Parser.Identifier - 68)))) != 0)): + break + + self.state = 518 + self.match(OpenSCENARIO2Parser.DEDENT) + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.state = 520 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ScenarioInheritsContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def qualifiedBehaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.QualifiedBehaviorNameContext, 0) + + def inheritsCondition(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.InheritsConditionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_scenarioInherits + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterScenarioInherits"): + listener.enterScenarioInherits(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitScenarioInherits"): + listener.exitScenarioInherits(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitScenarioInherits"): + return visitor.visitScenarioInherits(self) + else: + return visitor.visitChildren(self) + + def scenarioInherits(self): + + localctx = OpenSCENARIO2Parser.ScenarioInheritsContext(self, self._ctx, self.state) + self.enterRule(localctx, 72, self.RULE_scenarioInherits) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 523 + self.match(OpenSCENARIO2Parser.T__24) + self.state = 524 + self.qualifiedBehaviorName() + self.state = 526 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.OPEN_PAREN: + self.state = 525 + self.inheritsCondition() + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ScenarioMemberDeclContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventDeclarationContext, 0) + + def fieldDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldDeclarationContext, 0) + + def constraintDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ConstraintDeclarationContext, 0) + + def methodDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.MethodDeclarationContext, 0) + + def coverageDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CoverageDeclarationContext, 0) + + def modifierInvocation(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ModifierInvocationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_scenarioMemberDecl + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterScenarioMemberDecl"): + listener.enterScenarioMemberDecl(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitScenarioMemberDecl"): + listener.exitScenarioMemberDecl(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitScenarioMemberDecl"): + return visitor.visitScenarioMemberDecl(self) + else: + return visitor.visitChildren(self) + + def scenarioMemberDecl(self): + + localctx = OpenSCENARIO2Parser.ScenarioMemberDeclContext(self, self._ctx, self.state) + self.enterRule(localctx, 74, self.RULE_scenarioMemberDecl) + try: + self.state = 534 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 30, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 528 + self.eventDeclaration() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 529 + self.fieldDeclaration() + pass + + elif la_ == 3: + self.enterOuterAlt(localctx, 3) + self.state = 530 + self.constraintDeclaration() + pass + + elif la_ == 4: + self.enterOuterAlt(localctx, 4) + self.state = 531 + self.methodDeclaration() + pass + + elif la_ == 5: + self.enterOuterAlt(localctx, 5) + self.state = 532 + self.coverageDeclaration() + pass + + elif la_ == 6: + self.enterOuterAlt(localctx, 6) + self.state = 533 + self.modifierInvocation() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class QualifiedBehaviorNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def behaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorNameContext, 0) + + def actorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_qualifiedBehaviorName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterQualifiedBehaviorName"): + listener.enterQualifiedBehaviorName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitQualifiedBehaviorName"): + listener.exitQualifiedBehaviorName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitQualifiedBehaviorName"): + return visitor.visitQualifiedBehaviorName(self) + else: + return visitor.visitChildren(self) + + def qualifiedBehaviorName(self): + + localctx = OpenSCENARIO2Parser.QualifiedBehaviorNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 76, self.RULE_qualifiedBehaviorName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 539 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 31, self._ctx) + if la_ == 1: + self.state = 536 + self.actorName() + self.state = 537 + self.match(OpenSCENARIO2Parser.T__1) + + self.state = 541 + self.behaviorName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class BehaviorNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_behaviorName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBehaviorName"): + listener.enterBehaviorName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBehaviorName"): + listener.exitBehaviorName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBehaviorName"): + return visitor.visitBehaviorName(self) + else: + return visitor.visitChildren(self) + + def behaviorName(self): + + localctx = OpenSCENARIO2Parser.BehaviorNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 78, self.RULE_behaviorName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 543 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ActionDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def qualifiedBehaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.QualifiedBehaviorNameContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def actionInherits(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActionInheritsContext, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def scenarioMemberDecl(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ScenarioMemberDeclContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ScenarioMemberDeclContext, i) + + def behaviorSpecification(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.BehaviorSpecificationContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorSpecificationContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_actionDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterActionDeclaration"): + listener.enterActionDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitActionDeclaration"): + listener.exitActionDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitActionDeclaration"): + return visitor.visitActionDeclaration(self) + else: + return visitor.visitChildren(self) + + def actionDeclaration(self): + + localctx = OpenSCENARIO2Parser.ActionDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 80, self.RULE_actionDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 545 + self.match(OpenSCENARIO2Parser.T__27) + self.state = 546 + self.qualifiedBehaviorName() + self.state = 548 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__24: + self.state = 547 + self.actionInherits() + + self.state = 562 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__8]: + self.state = 550 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 551 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 552 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 555 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 555 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__37, OpenSCENARIO2Parser.T__45, OpenSCENARIO2Parser.T__48, OpenSCENARIO2Parser.T__51, OpenSCENARIO2Parser.T__61, OpenSCENARIO2Parser.T__67, OpenSCENARIO2Parser.T__68, OpenSCENARIO2Parser.Identifier]: + self.state = 553 + self.scenarioMemberDecl() + pass + elif token in [OpenSCENARIO2Parser.T__52, OpenSCENARIO2Parser.T__53]: + self.state = 554 + self.behaviorSpecification() + pass + else: + raise NoViableAltException(self) + + self.state = 557 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__37) | (1 << OpenSCENARIO2Parser.T__45) | (1 << OpenSCENARIO2Parser.T__48) | (1 << OpenSCENARIO2Parser.T__51) | (1 << OpenSCENARIO2Parser.T__52) | (1 << OpenSCENARIO2Parser.T__53) | (1 << OpenSCENARIO2Parser.T__61))) != 0) or ((((_la - 68)) & ~0x3f) == 0 and ((1 << (_la - 68)) & ((1 << (OpenSCENARIO2Parser.T__67 - 68)) | (1 << (OpenSCENARIO2Parser.T__68 - 68)) | (1 << (OpenSCENARIO2Parser.Identifier - 68)))) != 0)): + break + + self.state = 559 + self.match(OpenSCENARIO2Parser.DEDENT) + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.state = 561 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ActionInheritsContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def qualifiedBehaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.QualifiedBehaviorNameContext, 0) + + def inheritsCondition(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.InheritsConditionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_actionInherits + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterActionInherits"): + listener.enterActionInherits(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitActionInherits"): + listener.exitActionInherits(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitActionInherits"): + return visitor.visitActionInherits(self) + else: + return visitor.visitChildren(self) + + def actionInherits(self): + + localctx = OpenSCENARIO2Parser.ActionInheritsContext(self, self._ctx, self.state) + self.enterRule(localctx, 82, self.RULE_actionInherits) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 564 + self.match(OpenSCENARIO2Parser.T__24) + self.state = 565 + self.qualifiedBehaviorName() + self.state = 567 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.OPEN_PAREN: + self.state = 566 + self.inheritsCondition() + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ModifierDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def modifierName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ModifierNameContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def actorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorNameContext, 0) + + def qualifiedBehaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.QualifiedBehaviorNameContext, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def scenarioMemberDecl(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ScenarioMemberDeclContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ScenarioMemberDeclContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_modifierDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterModifierDeclaration"): + listener.enterModifierDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitModifierDeclaration"): + listener.exitModifierDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitModifierDeclaration"): + return visitor.visitModifierDeclaration(self) + else: + return visitor.visitChildren(self) + + def modifierDeclaration(self): + + localctx = OpenSCENARIO2Parser.ModifierDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 84, self.RULE_modifierDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 569 + self.match(OpenSCENARIO2Parser.T__28) + self.state = 573 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 37, self._ctx) + if la_ == 1: + self.state = 570 + self.actorName() + self.state = 571 + self.match(OpenSCENARIO2Parser.T__1) + + self.state = 575 + self.modifierName() + self.state = 578 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__6: + self.state = 576 + self.match(OpenSCENARIO2Parser.T__6) + self.state = 577 + self.qualifiedBehaviorName() + + self.state = 591 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__8]: + self.state = 580 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 581 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 582 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 584 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 583 + self.scenarioMemberDecl() + self.state = 586 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__37) | (1 << OpenSCENARIO2Parser.T__45) | (1 << OpenSCENARIO2Parser.T__48) | (1 << OpenSCENARIO2Parser.T__51) | (1 << OpenSCENARIO2Parser.T__61))) != 0) or ((((_la - 68)) & ~0x3f) == 0 and ((1 << (_la - 68)) & ((1 << (OpenSCENARIO2Parser.T__67 - 68)) | (1 << (OpenSCENARIO2Parser.T__68 - 68)) | (1 << (OpenSCENARIO2Parser.Identifier - 68)))) != 0)): + break + + self.state = 588 + self.match(OpenSCENARIO2Parser.DEDENT) + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.state = 590 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ModifierNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_modifierName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterModifierName"): + listener.enterModifierName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitModifierName"): + listener.exitModifierName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitModifierName"): + return visitor.visitModifierName(self) + else: + return visitor.visitChildren(self) + + def modifierName(self): + + localctx = OpenSCENARIO2Parser.ModifierNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 86, self.RULE_modifierName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 593 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class TypeExtensionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def enumTypeExtension(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumTypeExtensionContext, 0) + + def structuredTypeExtension(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructuredTypeExtensionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_typeExtension + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTypeExtension"): + listener.enterTypeExtension(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTypeExtension"): + listener.exitTypeExtension(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTypeExtension"): + return visitor.visitTypeExtension(self) + else: + return visitor.visitChildren(self) + + def typeExtension(self): + + localctx = OpenSCENARIO2Parser.TypeExtensionContext(self, self._ctx, self.state) + self.enterRule(localctx, 88, self.RULE_typeExtension) + try: + self.state = 597 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 41, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 595 + self.enumTypeExtension() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 596 + self.structuredTypeExtension() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EnumTypeExtensionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def enumName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumNameContext, 0) + + def OPEN_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_BRACK, 0) + + def enumMemberDecl(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.EnumMemberDeclContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumMemberDeclContext, i) + + def CLOSE_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_BRACK, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_enumTypeExtension + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEnumTypeExtension"): + listener.enterEnumTypeExtension(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEnumTypeExtension"): + listener.exitEnumTypeExtension(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEnumTypeExtension"): + return visitor.visitEnumTypeExtension(self) + else: + return visitor.visitChildren(self) + + def enumTypeExtension(self): + + localctx = OpenSCENARIO2Parser.EnumTypeExtensionContext(self, self._ctx, self.state) + self.enterRule(localctx, 90, self.RULE_enumTypeExtension) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 599 + self.match(OpenSCENARIO2Parser.T__29) + self.state = 600 + self.enumName() + self.state = 601 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 602 + self.match(OpenSCENARIO2Parser.OPEN_BRACK) + self.state = 603 + self.enumMemberDecl() + self.state = 608 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 604 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 605 + self.enumMemberDecl() + self.state = 610 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 611 + self.match(OpenSCENARIO2Parser.CLOSE_BRACK) + self.state = 612 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class StructuredTypeExtensionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def extendableTypeName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExtendableTypeNameContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def extensionMemberDecl(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ExtensionMemberDeclContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExtensionMemberDeclContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_structuredTypeExtension + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterStructuredTypeExtension"): + listener.enterStructuredTypeExtension(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitStructuredTypeExtension"): + listener.exitStructuredTypeExtension(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitStructuredTypeExtension"): + return visitor.visitStructuredTypeExtension(self) + else: + return visitor.visitChildren(self) + + def structuredTypeExtension(self): + + localctx = OpenSCENARIO2Parser.StructuredTypeExtensionContext(self, self._ctx, self.state) + self.enterRule(localctx, 92, self.RULE_structuredTypeExtension) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 614 + self.match(OpenSCENARIO2Parser.T__29) + self.state = 615 + self.extendableTypeName() + self.state = 616 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 617 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 618 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 620 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 619 + self.extensionMemberDecl() + self.state = 622 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__37) | (1 << OpenSCENARIO2Parser.T__45) | (1 << OpenSCENARIO2Parser.T__48) | (1 << OpenSCENARIO2Parser.T__51) | (1 << OpenSCENARIO2Parser.T__52) | (1 << OpenSCENARIO2Parser.T__53) | (1 << OpenSCENARIO2Parser.T__61))) != 0) or ((((_la - 68)) & ~0x3f) == 0 and ((1 << (_la - 68)) & ((1 << (OpenSCENARIO2Parser.T__67 - 68)) | (1 << (OpenSCENARIO2Parser.T__68 - 68)) | (1 << (OpenSCENARIO2Parser.Identifier - 68)))) != 0)): + break + + self.state = 624 + self.match(OpenSCENARIO2Parser.DEDENT) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ExtendableTypeNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def typeName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeNameContext, 0) + + def qualifiedBehaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.QualifiedBehaviorNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_extendableTypeName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterExtendableTypeName"): + listener.enterExtendableTypeName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitExtendableTypeName"): + listener.exitExtendableTypeName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitExtendableTypeName"): + return visitor.visitExtendableTypeName(self) + else: + return visitor.visitChildren(self) + + def extendableTypeName(self): + + localctx = OpenSCENARIO2Parser.ExtendableTypeNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 94, self.RULE_extendableTypeName) + try: + self.state = 628 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 44, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 626 + self.typeName() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 627 + self.qualifiedBehaviorName() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ExtensionMemberDeclContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def structMemberDecl(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructMemberDeclContext, 0) + + def actorMemberDecl(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorMemberDeclContext, 0) + + def scenarioMemberDecl(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ScenarioMemberDeclContext, 0) + + def behaviorSpecification(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorSpecificationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_extensionMemberDecl + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterExtensionMemberDecl"): + listener.enterExtensionMemberDecl(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitExtensionMemberDecl"): + listener.exitExtensionMemberDecl(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitExtensionMemberDecl"): + return visitor.visitExtensionMemberDecl(self) + else: + return visitor.visitChildren(self) + + def extensionMemberDecl(self): + + localctx = OpenSCENARIO2Parser.ExtensionMemberDeclContext(self, self._ctx, self.state) + self.enterRule(localctx, 96, self.RULE_extensionMemberDecl) + try: + self.state = 634 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 45, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 630 + self.structMemberDecl() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 631 + self.actorMemberDecl() + pass + + elif la_ == 3: + self.enterOuterAlt(localctx, 3) + self.state = 632 + self.scenarioMemberDecl() + pass + + elif la_ == 4: + self.enterOuterAlt(localctx, 4) + self.state = 633 + self.behaviorSpecification() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class GlobalParameterDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def fieldName(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.FieldNameContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldNameContext, i) + + def typeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeDeclaratorContext, 0) + + def parameterWithDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ParameterWithDeclarationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def defaultValue(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.DefaultValueContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_globalParameterDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterGlobalParameterDeclaration"): + listener.enterGlobalParameterDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitGlobalParameterDeclaration"): + listener.exitGlobalParameterDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitGlobalParameterDeclaration"): + return visitor.visitGlobalParameterDeclaration(self) + else: + return visitor.visitChildren(self) + + def globalParameterDeclaration(self): + + localctx = OpenSCENARIO2Parser.GlobalParameterDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 98, self.RULE_globalParameterDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 636 + self.match(OpenSCENARIO2Parser.T__30) + self.state = 637 + self.fieldName() + self.state = 642 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 638 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 639 + self.fieldName() + self.state = 644 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 645 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 646 + self.typeDeclarator() + self.state = 649 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__20: + self.state = 647 + self.match(OpenSCENARIO2Parser.T__20) + self.state = 648 + self.defaultValue() + + self.state = 653 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__47]: + self.state = 651 + self.parameterWithDeclaration() + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.state = 652 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class TypeDeclaratorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def nonAggregateTypeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.NonAggregateTypeDeclaratorContext, 0) + + def aggregateTypeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.AggregateTypeDeclaratorContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_typeDeclarator + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTypeDeclarator"): + listener.enterTypeDeclarator(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTypeDeclarator"): + listener.exitTypeDeclarator(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTypeDeclarator"): + return visitor.visitTypeDeclarator(self) + else: + return visitor.visitChildren(self) + + def typeDeclarator(self): + + localctx = OpenSCENARIO2Parser.TypeDeclaratorContext(self, self._ctx, self.state) + self.enterRule(localctx, 100, self.RULE_typeDeclarator) + try: + self.state = 657 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__32, OpenSCENARIO2Parser.T__33, OpenSCENARIO2Parser.T__34, OpenSCENARIO2Parser.T__35, OpenSCENARIO2Parser.T__36, OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 1) + self.state = 655 + self.nonAggregateTypeDeclarator() + pass + elif token in [OpenSCENARIO2Parser.T__31]: + self.enterOuterAlt(localctx, 2) + self.state = 656 + self.aggregateTypeDeclarator() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class NonAggregateTypeDeclaratorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def primitiveType(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PrimitiveTypeContext, 0) + + def typeName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeNameContext, 0) + + def qualifiedBehaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.QualifiedBehaviorNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_nonAggregateTypeDeclarator + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterNonAggregateTypeDeclarator"): + listener.enterNonAggregateTypeDeclarator(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitNonAggregateTypeDeclarator"): + listener.exitNonAggregateTypeDeclarator(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitNonAggregateTypeDeclarator"): + return visitor.visitNonAggregateTypeDeclarator(self) + else: + return visitor.visitChildren(self) + + def nonAggregateTypeDeclarator(self): + + localctx = OpenSCENARIO2Parser.NonAggregateTypeDeclaratorContext(self, self._ctx, self.state) + self.enterRule(localctx, 102, self.RULE_nonAggregateTypeDeclarator) + try: + self.state = 662 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 50, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 659 + self.primitiveType() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 660 + self.typeName() + pass + + elif la_ == 3: + self.enterOuterAlt(localctx, 3) + self.state = 661 + self.qualifiedBehaviorName() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class AggregateTypeDeclaratorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def listTypeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ListTypeDeclaratorContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_aggregateTypeDeclarator + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterAggregateTypeDeclarator"): + listener.enterAggregateTypeDeclarator(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitAggregateTypeDeclarator"): + listener.exitAggregateTypeDeclarator(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitAggregateTypeDeclarator"): + return visitor.visitAggregateTypeDeclarator(self) + else: + return visitor.visitChildren(self) + + def aggregateTypeDeclarator(self): + + localctx = OpenSCENARIO2Parser.AggregateTypeDeclaratorContext(self, self._ctx, self.state) + self.enterRule(localctx, 104, self.RULE_aggregateTypeDeclarator) + try: + self.enterOuterAlt(localctx, 1) + self.state = 664 + self.listTypeDeclarator() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ListTypeDeclaratorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def nonAggregateTypeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.NonAggregateTypeDeclaratorContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_listTypeDeclarator + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterListTypeDeclarator"): + listener.enterListTypeDeclarator(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitListTypeDeclarator"): + listener.exitListTypeDeclarator(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitListTypeDeclarator"): + return visitor.visitListTypeDeclarator(self) + else: + return visitor.visitChildren(self) + + def listTypeDeclarator(self): + + localctx = OpenSCENARIO2Parser.ListTypeDeclaratorContext(self, self._ctx, self.state) + self.enterRule(localctx, 106, self.RULE_listTypeDeclarator) + try: + self.enterOuterAlt(localctx, 1) + self.state = 666 + self.match(OpenSCENARIO2Parser.T__31) + self.state = 667 + self.match(OpenSCENARIO2Parser.T__6) + self.state = 668 + self.nonAggregateTypeDeclarator() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class PrimitiveTypeContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_primitiveType + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPrimitiveType"): + listener.enterPrimitiveType(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPrimitiveType"): + listener.exitPrimitiveType(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPrimitiveType"): + return visitor.visitPrimitiveType(self) + else: + return visitor.visitChildren(self) + + def primitiveType(self): + + localctx = OpenSCENARIO2Parser.PrimitiveTypeContext(self, self._ctx, self.state) + self.enterRule(localctx, 108, self.RULE_primitiveType) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 670 + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__32) | (1 << OpenSCENARIO2Parser.T__33) | (1 << OpenSCENARIO2Parser.T__34) | (1 << OpenSCENARIO2Parser.T__35) | (1 << OpenSCENARIO2Parser.T__36))) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class TypeNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_typeName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTypeName"): + listener.enterTypeName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTypeName"): + listener.exitTypeName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTypeName"): + return visitor.visitTypeName(self) + else: + return visitor.visitChildren(self) + + def typeName(self): + + localctx = OpenSCENARIO2Parser.TypeNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 110, self.RULE_typeName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 672 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EventDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventNameContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def argumentListSpecification(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListSpecificationContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def eventSpecification(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventSpecificationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_eventDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEventDeclaration"): + listener.enterEventDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEventDeclaration"): + listener.exitEventDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEventDeclaration"): + return visitor.visitEventDeclaration(self) + else: + return visitor.visitChildren(self) + + def eventDeclaration(self): + + localctx = OpenSCENARIO2Parser.EventDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 112, self.RULE_eventDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 674 + self.match(OpenSCENARIO2Parser.T__37) + self.state = 675 + self.eventName() + self.state = 680 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.OPEN_PAREN: + self.state = 676 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 677 + self.argumentListSpecification() + self.state = 678 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + + self.state = 684 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__3: + self.state = 682 + self.match(OpenSCENARIO2Parser.T__3) + self.state = 683 + self.eventSpecification() + + self.state = 686 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EventSpecificationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventReference(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventReferenceContext, 0) + + def eventCondition(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventConditionContext, 0) + + def eventFieldDecl(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventFieldDeclContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_eventSpecification + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEventSpecification"): + listener.enterEventSpecification(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEventSpecification"): + listener.exitEventSpecification(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEventSpecification"): + return visitor.visitEventSpecification(self) + else: + return visitor.visitChildren(self) + + def eventSpecification(self): + + localctx = OpenSCENARIO2Parser.EventSpecificationContext(self, self._ctx, self.state) + self.enterRule(localctx, 114, self.RULE_eventSpecification) + self._la = 0 # Token type + try: + self.state = 697 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__39]: + self.enterOuterAlt(localctx, 1) + self.state = 688 + self.eventReference() + self.state = 694 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__38 or _la == OpenSCENARIO2Parser.T__40: + self.state = 690 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__40: + self.state = 689 + self.eventFieldDecl() + + self.state = 692 + self.match(OpenSCENARIO2Parser.T__38) + self.state = 693 + self.eventCondition() + + pass + elif token in [OpenSCENARIO2Parser.T__41, OpenSCENARIO2Parser.T__42, OpenSCENARIO2Parser.T__43, OpenSCENARIO2Parser.T__44, OpenSCENARIO2Parser.T__69, OpenSCENARIO2Parser.T__74, OpenSCENARIO2Parser.T__82, OpenSCENARIO2Parser.T__86, OpenSCENARIO2Parser.OPEN_BRACK, OpenSCENARIO2Parser.OPEN_PAREN, OpenSCENARIO2Parser.StringLiteral, OpenSCENARIO2Parser.FloatLiteral, OpenSCENARIO2Parser.UintLiteral, OpenSCENARIO2Parser.HexUintLiteral, OpenSCENARIO2Parser.IntLiteral, OpenSCENARIO2Parser.BoolLiteral, OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 2) + self.state = 696 + self.eventCondition() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EventReferenceContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventPath(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventPathContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_eventReference + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEventReference"): + listener.enterEventReference(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEventReference"): + listener.exitEventReference(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEventReference"): + return visitor.visitEventReference(self) + else: + return visitor.visitChildren(self) + + def eventReference(self): + + localctx = OpenSCENARIO2Parser.EventReferenceContext(self, self._ctx, self.state) + self.enterRule(localctx, 116, self.RULE_eventReference) + try: + self.enterOuterAlt(localctx, 1) + self.state = 699 + self.match(OpenSCENARIO2Parser.T__39) + self.state = 700 + self.eventPath() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EventFieldDeclContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventFieldName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventFieldNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_eventFieldDecl + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEventFieldDecl"): + listener.enterEventFieldDecl(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEventFieldDecl"): + listener.exitEventFieldDecl(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEventFieldDecl"): + return visitor.visitEventFieldDecl(self) + else: + return visitor.visitChildren(self) + + def eventFieldDecl(self): + + localctx = OpenSCENARIO2Parser.EventFieldDeclContext(self, self._ctx, self.state) + self.enterRule(localctx, 118, self.RULE_eventFieldDecl) + try: + self.enterOuterAlt(localctx, 1) + self.state = 702 + self.match(OpenSCENARIO2Parser.T__40) + self.state = 703 + self.eventFieldName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EventFieldNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_eventFieldName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEventFieldName"): + listener.enterEventFieldName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEventFieldName"): + listener.exitEventFieldName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEventFieldName"): + return visitor.visitEventFieldName(self) + else: + return visitor.visitChildren(self) + + def eventFieldName(self): + + localctx = OpenSCENARIO2Parser.EventFieldNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 120, self.RULE_eventFieldName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 705 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EventNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_eventName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEventName"): + listener.enterEventName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEventName"): + listener.exitEventName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEventName"): + return visitor.visitEventName(self) + else: + return visitor.visitChildren(self) + + def eventName(self): + + localctx = OpenSCENARIO2Parser.EventNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 122, self.RULE_eventName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 707 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EventPathContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventNameContext, 0) + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_eventPath + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEventPath"): + listener.enterEventPath(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEventPath"): + listener.exitEventPath(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEventPath"): + return visitor.visitEventPath(self) + else: + return visitor.visitChildren(self) + + def eventPath(self): + + localctx = OpenSCENARIO2Parser.EventPathContext(self, self._ctx, self.state) + self.enterRule(localctx, 124, self.RULE_eventPath) + try: + self.enterOuterAlt(localctx, 1) + self.state = 712 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 56, self._ctx) + if la_ == 1: + self.state = 709 + self.expression() + self.state = 710 + self.match(OpenSCENARIO2Parser.T__1) + + self.state = 714 + self.eventName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EventConditionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def boolExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BoolExpressionContext, 0) + + def riseExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.RiseExpressionContext, 0) + + def fallExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FallExpressionContext, 0) + + def elapsedExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ElapsedExpressionContext, 0) + + def everyExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EveryExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_eventCondition + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEventCondition"): + listener.enterEventCondition(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEventCondition"): + listener.exitEventCondition(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEventCondition"): + return visitor.visitEventCondition(self) + else: + return visitor.visitChildren(self) + + def eventCondition(self): + + localctx = OpenSCENARIO2Parser.EventConditionContext(self, self._ctx, self.state) + self.enterRule(localctx, 126, self.RULE_eventCondition) + try: + self.state = 721 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__69, OpenSCENARIO2Parser.T__74, OpenSCENARIO2Parser.T__82, OpenSCENARIO2Parser.T__86, OpenSCENARIO2Parser.OPEN_BRACK, OpenSCENARIO2Parser.OPEN_PAREN, OpenSCENARIO2Parser.StringLiteral, OpenSCENARIO2Parser.FloatLiteral, OpenSCENARIO2Parser.UintLiteral, OpenSCENARIO2Parser.HexUintLiteral, OpenSCENARIO2Parser.IntLiteral, OpenSCENARIO2Parser.BoolLiteral, OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 1) + self.state = 716 + self.boolExpression() + pass + elif token in [OpenSCENARIO2Parser.T__41]: + self.enterOuterAlt(localctx, 2) + self.state = 717 + self.riseExpression() + pass + elif token in [OpenSCENARIO2Parser.T__42]: + self.enterOuterAlt(localctx, 3) + self.state = 718 + self.fallExpression() + pass + elif token in [OpenSCENARIO2Parser.T__43]: + self.enterOuterAlt(localctx, 4) + self.state = 719 + self.elapsedExpression() + pass + elif token in [OpenSCENARIO2Parser.T__44]: + self.enterOuterAlt(localctx, 5) + self.state = 720 + self.everyExpression() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class RiseExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def boolExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BoolExpressionContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_riseExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterRiseExpression"): + listener.enterRiseExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitRiseExpression"): + listener.exitRiseExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitRiseExpression"): + return visitor.visitRiseExpression(self) + else: + return visitor.visitChildren(self) + + def riseExpression(self): + + localctx = OpenSCENARIO2Parser.RiseExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 128, self.RULE_riseExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 723 + self.match(OpenSCENARIO2Parser.T__41) + self.state = 724 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 725 + self.boolExpression() + self.state = 726 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class FallExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def boolExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BoolExpressionContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_fallExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFallExpression"): + listener.enterFallExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFallExpression"): + listener.exitFallExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFallExpression"): + return visitor.visitFallExpression(self) + else: + return visitor.visitChildren(self) + + def fallExpression(self): + + localctx = OpenSCENARIO2Parser.FallExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 130, self.RULE_fallExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 728 + self.match(OpenSCENARIO2Parser.T__42) + self.state = 729 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 730 + self.boolExpression() + self.state = 731 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ElapsedExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def durationExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.DurationExpressionContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_elapsedExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterElapsedExpression"): + listener.enterElapsedExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitElapsedExpression"): + listener.exitElapsedExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitElapsedExpression"): + return visitor.visitElapsedExpression(self) + else: + return visitor.visitChildren(self) + + def elapsedExpression(self): + + localctx = OpenSCENARIO2Parser.ElapsedExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 132, self.RULE_elapsedExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 733 + self.match(OpenSCENARIO2Parser.T__43) + self.state = 734 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 735 + self.durationExpression() + self.state = 736 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EveryExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + self._Identifier = None # Token + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def durationExpression(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.DurationExpressionContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.DurationExpressionContext, i) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_everyExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEveryExpression"): + listener.enterEveryExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEveryExpression"): + listener.exitEveryExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEveryExpression"): + return visitor.visitEveryExpression(self) + else: + return visitor.visitChildren(self) + + def everyExpression(self): + + localctx = OpenSCENARIO2Parser.EveryExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 134, self.RULE_everyExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 738 + self.match(OpenSCENARIO2Parser.T__44) + self.state = 739 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 740 + self.durationExpression() + self.state = 746 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__7: + self.state = 741 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 742 + localctx._Identifier = self.match(OpenSCENARIO2Parser.Identifier) + + offsetName = (None if localctx._Identifier is None else localctx._Identifier.text) + if (not (offsetName == "offset")): + print("%s must be offset" % offsetName) + raise NoViableAltException(self) + + self.state = 744 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 745 + self.durationExpression() + + self.state = 748 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class BoolExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_boolExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBoolExpression"): + listener.enterBoolExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBoolExpression"): + listener.exitBoolExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBoolExpression"): + return visitor.visitBoolExpression(self) + else: + return visitor.visitChildren(self) + + def boolExpression(self): + + localctx = OpenSCENARIO2Parser.BoolExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 136, self.RULE_boolExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 750 + self.expression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class DurationExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_durationExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDurationExpression"): + listener.enterDurationExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDurationExpression"): + listener.exitDurationExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDurationExpression"): + return visitor.visitDurationExpression(self) + else: + return visitor.visitChildren(self) + + def durationExpression(self): + + localctx = OpenSCENARIO2Parser.DurationExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 138, self.RULE_durationExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 752 + self.expression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class FieldDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def parameterDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ParameterDeclarationContext, 0) + + def variableDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.VariableDeclarationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_fieldDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFieldDeclaration"): + listener.enterFieldDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFieldDeclaration"): + listener.exitFieldDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFieldDeclaration"): + return visitor.visitFieldDeclaration(self) + else: + return visitor.visitChildren(self) + + def fieldDeclaration(self): + + localctx = OpenSCENARIO2Parser.FieldDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 140, self.RULE_fieldDeclaration) + try: + self.state = 756 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 1) + self.state = 754 + self.parameterDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__45]: + self.enterOuterAlt(localctx, 2) + self.state = 755 + self.variableDeclaration() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ParameterDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def fieldName(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.FieldNameContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldNameContext, i) + + def typeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeDeclaratorContext, 0) + + def parameterWithDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ParameterWithDeclarationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def defaultValue(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.DefaultValueContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_parameterDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterParameterDeclaration"): + listener.enterParameterDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitParameterDeclaration"): + listener.exitParameterDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitParameterDeclaration"): + return visitor.visitParameterDeclaration(self) + else: + return visitor.visitChildren(self) + + def parameterDeclaration(self): + + localctx = OpenSCENARIO2Parser.ParameterDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 142, self.RULE_parameterDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 758 + self.fieldName() + self.state = 763 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 759 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 760 + self.fieldName() + self.state = 765 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 766 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 767 + self.typeDeclarator() + self.state = 770 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__20: + self.state = 768 + self.match(OpenSCENARIO2Parser.T__20) + self.state = 769 + self.defaultValue() + + self.state = 774 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__47]: + self.state = 772 + self.parameterWithDeclaration() + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.state = 773 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class VariableDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def fieldName(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.FieldNameContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldNameContext, i) + + def typeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeDeclaratorContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def sampleExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SampleExpressionContext, 0) + + def valueExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ValueExpContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_variableDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterVariableDeclaration"): + listener.enterVariableDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitVariableDeclaration"): + listener.exitVariableDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitVariableDeclaration"): + return visitor.visitVariableDeclaration(self) + else: + return visitor.visitChildren(self) + + def variableDeclaration(self): + + localctx = OpenSCENARIO2Parser.VariableDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 144, self.RULE_variableDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 776 + self.match(OpenSCENARIO2Parser.T__45) + self.state = 777 + self.fieldName() + self.state = 782 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 778 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 779 + self.fieldName() + self.state = 784 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 785 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 786 + self.typeDeclarator() + self.state = 792 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__20: + self.state = 787 + self.match(OpenSCENARIO2Parser.T__20) + self.state = 790 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__46]: + self.state = 788 + self.sampleExpression() + pass + elif token in [OpenSCENARIO2Parser.T__69, OpenSCENARIO2Parser.OPEN_BRACK, OpenSCENARIO2Parser.StringLiteral, OpenSCENARIO2Parser.FloatLiteral, OpenSCENARIO2Parser.UintLiteral, OpenSCENARIO2Parser.HexUintLiteral, OpenSCENARIO2Parser.IntLiteral, OpenSCENARIO2Parser.BoolLiteral, OpenSCENARIO2Parser.Identifier]: + self.state = 789 + self.valueExp() + pass + else: + raise NoViableAltException(self) + + self.state = 794 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SampleExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def eventSpecification(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventSpecificationContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def defaultValue(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.DefaultValueContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_sampleExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSampleExpression"): + listener.enterSampleExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSampleExpression"): + listener.exitSampleExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSampleExpression"): + return visitor.visitSampleExpression(self) + else: + return visitor.visitChildren(self) + + def sampleExpression(self): + + localctx = OpenSCENARIO2Parser.SampleExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 146, self.RULE_sampleExpression) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 796 + self.match(OpenSCENARIO2Parser.T__46) + self.state = 797 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 798 + self.expression() + self.state = 799 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 800 + self.eventSpecification() + self.state = 803 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__7: + self.state = 801 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 802 + self.defaultValue() + + self.state = 805 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class DefaultValueContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_defaultValue + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDefaultValue"): + listener.enterDefaultValue(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDefaultValue"): + listener.exitDefaultValue(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDefaultValue"): + return visitor.visitDefaultValue(self) + else: + return visitor.visitChildren(self) + + def defaultValue(self): + + localctx = OpenSCENARIO2Parser.DefaultValueContext(self, self._ctx, self.state) + self.enterRule(localctx, 148, self.RULE_defaultValue) + try: + self.enterOuterAlt(localctx, 1) + self.state = 807 + self.expression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ParameterWithDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def parameterWithMember(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ParameterWithMemberContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ParameterWithMemberContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_parameterWithDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterParameterWithDeclaration"): + listener.enterParameterWithDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitParameterWithDeclaration"): + listener.exitParameterWithDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitParameterWithDeclaration"): + return visitor.visitParameterWithDeclaration(self) + else: + return visitor.visitChildren(self) + + def parameterWithDeclaration(self): + + localctx = OpenSCENARIO2Parser.ParameterWithDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 150, self.RULE_parameterWithDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 809 + self.match(OpenSCENARIO2Parser.T__47) + self.state = 810 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 811 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 812 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 814 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 813 + self.parameterWithMember() + self.state = 816 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not (((((_la - 49)) & ~0x3f) == 0 and ((1 << (_la - 49)) & ((1 << (OpenSCENARIO2Parser.T__48 - 49)) | (1 << (OpenSCENARIO2Parser.T__51 - 49)) | (1 << (OpenSCENARIO2Parser.T__67 - 49)) | (1 << (OpenSCENARIO2Parser.T__68 - 49)))) != 0)): + break + + self.state = 818 + self.match(OpenSCENARIO2Parser.DEDENT) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ParameterWithMemberContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def constraintDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ConstraintDeclarationContext, 0) + + def coverageDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CoverageDeclarationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_parameterWithMember + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterParameterWithMember"): + listener.enterParameterWithMember(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitParameterWithMember"): + listener.exitParameterWithMember(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitParameterWithMember"): + return visitor.visitParameterWithMember(self) + else: + return visitor.visitChildren(self) + + def parameterWithMember(self): + + localctx = OpenSCENARIO2Parser.ParameterWithMemberContext(self, self._ctx, self.state) + self.enterRule(localctx, 152, self.RULE_parameterWithMember) + try: + self.state = 822 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__48, OpenSCENARIO2Parser.T__51]: + self.enterOuterAlt(localctx, 1) + self.state = 820 + self.constraintDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__67, OpenSCENARIO2Parser.T__68]: + self.enterOuterAlt(localctx, 2) + self.state = 821 + self.coverageDeclaration() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ConstraintDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def keepConstraintDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.KeepConstraintDeclarationContext, 0) + + def removeDefaultDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.RemoveDefaultDeclarationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_constraintDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterConstraintDeclaration"): + listener.enterConstraintDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitConstraintDeclaration"): + listener.exitConstraintDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitConstraintDeclaration"): + return visitor.visitConstraintDeclaration(self) + else: + return visitor.visitChildren(self) + + def constraintDeclaration(self): + + localctx = OpenSCENARIO2Parser.ConstraintDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 154, self.RULE_constraintDeclaration) + try: + self.state = 826 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__48]: + self.enterOuterAlt(localctx, 1) + self.state = 824 + self.keepConstraintDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__51]: + self.enterOuterAlt(localctx, 2) + self.state = 825 + self.removeDefaultDeclaration() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class KeepConstraintDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def constraintExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ConstraintExpressionContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def constraintQualifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ConstraintQualifierContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_keepConstraintDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterKeepConstraintDeclaration"): + listener.enterKeepConstraintDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitKeepConstraintDeclaration"): + listener.exitKeepConstraintDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitKeepConstraintDeclaration"): + return visitor.visitKeepConstraintDeclaration(self) + else: + return visitor.visitChildren(self) + + def keepConstraintDeclaration(self): + + localctx = OpenSCENARIO2Parser.KeepConstraintDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 156, self.RULE_keepConstraintDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 828 + self.match(OpenSCENARIO2Parser.T__48) + self.state = 829 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 831 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__49 or _la == OpenSCENARIO2Parser.T__50: + self.state = 830 + self.constraintQualifier() + + self.state = 833 + self.constraintExpression() + self.state = 834 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + self.state = 835 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ConstraintQualifierContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_constraintQualifier + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterConstraintQualifier"): + listener.enterConstraintQualifier(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitConstraintQualifier"): + listener.exitConstraintQualifier(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitConstraintQualifier"): + return visitor.visitConstraintQualifier(self) + else: + return visitor.visitChildren(self) + + def constraintQualifier(self): + + localctx = OpenSCENARIO2Parser.ConstraintQualifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 158, self.RULE_constraintQualifier) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 837 + _la = self._input.LA(1) + if not (_la == OpenSCENARIO2Parser.T__49 or _la == OpenSCENARIO2Parser.T__50): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ConstraintExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_constraintExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterConstraintExpression"): + listener.enterConstraintExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitConstraintExpression"): + listener.exitConstraintExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitConstraintExpression"): + return visitor.visitConstraintExpression(self) + else: + return visitor.visitChildren(self) + + def constraintExpression(self): + + localctx = OpenSCENARIO2Parser.ConstraintExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 160, self.RULE_constraintExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 839 + self.expression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class RemoveDefaultDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def parameterReference(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ParameterReferenceContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_removeDefaultDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterRemoveDefaultDeclaration"): + listener.enterRemoveDefaultDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitRemoveDefaultDeclaration"): + listener.exitRemoveDefaultDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitRemoveDefaultDeclaration"): + return visitor.visitRemoveDefaultDeclaration(self) + else: + return visitor.visitChildren(self) + + def removeDefaultDeclaration(self): + + localctx = OpenSCENARIO2Parser.RemoveDefaultDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 162, self.RULE_removeDefaultDeclaration) + try: + self.enterOuterAlt(localctx, 1) + self.state = 841 + self.match(OpenSCENARIO2Parser.T__51) + self.state = 842 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 843 + self.parameterReference() + self.state = 844 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + self.state = 845 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ParameterReferenceContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def fieldName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldNameContext, 0) + + def fieldAccess(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldAccessContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_parameterReference + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterParameterReference"): + listener.enterParameterReference(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitParameterReference"): + listener.exitParameterReference(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitParameterReference"): + return visitor.visitParameterReference(self) + else: + return visitor.visitChildren(self) + + def parameterReference(self): + + localctx = OpenSCENARIO2Parser.ParameterReferenceContext(self, self._ctx, self.state) + self.enterRule(localctx, 164, self.RULE_parameterReference) + try: + self.state = 849 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 71, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 847 + self.fieldName() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 848 + self.fieldAccess() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ModifierInvocationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def modifierName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ModifierNameContext, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def argumentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListContext, 0) + + def behaviorExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorExpressionContext, 0) + + def actorExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_modifierInvocation + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterModifierInvocation"): + listener.enterModifierInvocation(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitModifierInvocation"): + listener.exitModifierInvocation(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitModifierInvocation"): + return visitor.visitModifierInvocation(self) + else: + return visitor.visitChildren(self) + + def modifierInvocation(self): + + localctx = OpenSCENARIO2Parser.ModifierInvocationContext(self, self._ctx, self.state) + self.enterRule(localctx, 166, self.RULE_modifierInvocation) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 857 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 73, self._ctx) + if la_ == 1: + self.state = 853 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 72, self._ctx) + if la_ == 1: + self.state = 851 + self.behaviorExpression() + pass + + elif la_ == 2: + self.state = 852 + self.actorExpression() + pass + + self.state = 855 + self.match(OpenSCENARIO2Parser.T__1) + + self.state = 859 + self.modifierName() + self.state = 860 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 862 + self._errHandler.sync(self) + _la = self._input.LA(1) + if ((((_la - 70)) & ~0x3f) == 0 and ((1 << (_la - 70)) & ((1 << (OpenSCENARIO2Parser.T__69 - 70)) | (1 << (OpenSCENARIO2Parser.T__74 - 70)) | (1 << (OpenSCENARIO2Parser.T__82 - 70)) | (1 << (OpenSCENARIO2Parser.T__86 - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_BRACK - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_PAREN - 70)) | (1 << (OpenSCENARIO2Parser.StringLiteral - 70)) | (1 << (OpenSCENARIO2Parser.FloatLiteral - 70)) | (1 << (OpenSCENARIO2Parser.UintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.HexUintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.IntLiteral - 70)) | (1 << (OpenSCENARIO2Parser.BoolLiteral - 70)) | (1 << (OpenSCENARIO2Parser.Identifier - 70)))) != 0): + self.state = 861 + self.argumentList() + + self.state = 864 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + self.state = 865 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class BehaviorExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def behaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorNameContext, 0) + + def actorExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_behaviorExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBehaviorExpression"): + listener.enterBehaviorExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBehaviorExpression"): + listener.exitBehaviorExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBehaviorExpression"): + return visitor.visitBehaviorExpression(self) + else: + return visitor.visitChildren(self) + + def behaviorExpression(self): + + localctx = OpenSCENARIO2Parser.BehaviorExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 168, self.RULE_behaviorExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 867 + self.actorExpression() + self.state = 868 + self.match(OpenSCENARIO2Parser.T__1) + self.state = 870 + self.behaviorName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class BehaviorSpecificationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def onDirective(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.OnDirectiveContext, 0) + + def doDirective(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.DoDirectiveContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_behaviorSpecification + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBehaviorSpecification"): + listener.enterBehaviorSpecification(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBehaviorSpecification"): + listener.exitBehaviorSpecification(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBehaviorSpecification"): + return visitor.visitBehaviorSpecification(self) + else: + return visitor.visitChildren(self) + + def behaviorSpecification(self): + + localctx = OpenSCENARIO2Parser.BehaviorSpecificationContext(self, self._ctx, self.state) + self.enterRule(localctx, 170, self.RULE_behaviorSpecification) + try: + self.state = 874 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__52]: + self.enterOuterAlt(localctx, 1) + self.state = 872 + self.onDirective() + pass + elif token in [OpenSCENARIO2Parser.T__53]: + self.enterOuterAlt(localctx, 2) + self.state = 873 + self.doDirective() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class OnDirectiveContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventSpecification(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventSpecificationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def onMember(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.OnMemberContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.OnMemberContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_onDirective + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterOnDirective"): + listener.enterOnDirective(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitOnDirective"): + listener.exitOnDirective(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitOnDirective"): + return visitor.visitOnDirective(self) + else: + return visitor.visitChildren(self) + + def onDirective(self): + + localctx = OpenSCENARIO2Parser.OnDirectiveContext(self, self._ctx, self.state) + self.enterRule(localctx, 172, self.RULE_onDirective) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 876 + self.match(OpenSCENARIO2Parser.T__52) + self.state = 877 + self.eventSpecification() + self.state = 878 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 879 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 880 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 882 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 881 + self.onMember() + self.state = 884 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not (_la == OpenSCENARIO2Parser.T__58 or _la == OpenSCENARIO2Parser.T__59): + break + + self.state = 886 + self.match(OpenSCENARIO2Parser.DEDENT) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class OnMemberContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def callDirective(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CallDirectiveContext, 0) + + def emitDirective(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EmitDirectiveContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_onMember + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterOnMember"): + listener.enterOnMember(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitOnMember"): + listener.exitOnMember(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitOnMember"): + return visitor.visitOnMember(self) + else: + return visitor.visitChildren(self) + + def onMember(self): + + localctx = OpenSCENARIO2Parser.OnMemberContext(self, self._ctx, self.state) + self.enterRule(localctx, 174, self.RULE_onMember) + try: + self.state = 890 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__59]: + self.enterOuterAlt(localctx, 1) + self.state = 888 + self.callDirective() + pass + elif token in [OpenSCENARIO2Parser.T__58]: + self.enterOuterAlt(localctx, 2) + self.state = 889 + self.emitDirective() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class DoDirectiveContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def doMember(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.DoMemberContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_doDirective + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDoDirective"): + listener.enterDoDirective(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDoDirective"): + listener.exitDoDirective(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDoDirective"): + return visitor.visitDoDirective(self) + else: + return visitor.visitChildren(self) + + def doDirective(self): + + localctx = OpenSCENARIO2Parser.DoDirectiveContext(self, self._ctx, self.state) + self.enterRule(localctx, 176, self.RULE_doDirective) + try: + self.enterOuterAlt(localctx, 1) + self.state = 892 + self.match(OpenSCENARIO2Parser.T__53) + self.state = 893 + self.doMember() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class DoMemberContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def composition(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CompositionContext, 0) + + def behaviorInvocation(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorInvocationContext, 0) + + def waitDirective(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.WaitDirectiveContext, 0) + + def emitDirective(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EmitDirectiveContext, 0) + + def callDirective(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CallDirectiveContext, 0) + + def labelName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.LabelNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_doMember + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDoMember"): + listener.enterDoMember(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDoMember"): + listener.exitDoMember(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDoMember"): + return visitor.visitDoMember(self) + else: + return visitor.visitChildren(self) + + def doMember(self): + + localctx = OpenSCENARIO2Parser.DoMemberContext(self, self._ctx, self.state) + self.enterRule(localctx, 178, self.RULE_doMember) + try: + self.enterOuterAlt(localctx, 1) + self.state = 898 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 78, self._ctx) + if la_ == 1: + self.state = 895 + self.labelName() + self.state = 896 + self.match(OpenSCENARIO2Parser.T__8) + + self.state = 905 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__54, OpenSCENARIO2Parser.T__55, OpenSCENARIO2Parser.T__56]: + self.state = 900 + self.composition() + pass + elif token in [OpenSCENARIO2Parser.Identifier]: + self.state = 901 + self.behaviorInvocation() + pass + elif token in [OpenSCENARIO2Parser.T__57]: + self.state = 902 + self.waitDirective() + pass + elif token in [OpenSCENARIO2Parser.T__58]: + self.state = 903 + self.emitDirective() + pass + elif token in [OpenSCENARIO2Parser.T__59]: + self.state = 904 + self.callDirective() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class CompositionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def compositionOperator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CompositionOperatorContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def doMember(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.DoMemberContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.DoMemberContext, i) + + def behaviorWithDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorWithDeclarationContext, 0) + + def argumentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_composition + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterComposition"): + listener.enterComposition(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitComposition"): + listener.exitComposition(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitComposition"): + return visitor.visitComposition(self) + else: + return visitor.visitChildren(self) + + def composition(self): + + localctx = OpenSCENARIO2Parser.CompositionContext(self, self._ctx, self.state) + self.enterRule(localctx, 180, self.RULE_composition) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 907 + self.compositionOperator() + self.state = 913 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.OPEN_PAREN: + self.state = 908 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 910 + self._errHandler.sync(self) + _la = self._input.LA(1) + if ((((_la - 70)) & ~0x3f) == 0 and ((1 << (_la - 70)) & ((1 << (OpenSCENARIO2Parser.T__69 - 70)) | (1 << (OpenSCENARIO2Parser.T__74 - 70)) | (1 << (OpenSCENARIO2Parser.T__82 - 70)) | (1 << (OpenSCENARIO2Parser.T__86 - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_BRACK - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_PAREN - 70)) | (1 << (OpenSCENARIO2Parser.StringLiteral - 70)) | (1 << (OpenSCENARIO2Parser.FloatLiteral - 70)) | (1 << (OpenSCENARIO2Parser.UintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.HexUintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.IntLiteral - 70)) | (1 << (OpenSCENARIO2Parser.BoolLiteral - 70)) | (1 << (OpenSCENARIO2Parser.Identifier - 70)))) != 0): + self.state = 909 + self.argumentList() + + self.state = 912 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + + self.state = 915 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 916 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 917 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 919 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 918 + self.doMember() + self.state = 921 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not (((((_la - 55)) & ~0x3f) == 0 and ((1 << (_la - 55)) & ((1 << (OpenSCENARIO2Parser.T__54 - 55)) | (1 << (OpenSCENARIO2Parser.T__55 - 55)) | (1 << (OpenSCENARIO2Parser.T__56 - 55)) | (1 << (OpenSCENARIO2Parser.T__57 - 55)) | (1 << (OpenSCENARIO2Parser.T__58 - 55)) | (1 << (OpenSCENARIO2Parser.T__59 - 55)) | (1 << (OpenSCENARIO2Parser.Identifier - 55)))) != 0)): + break + + self.state = 923 + self.match(OpenSCENARIO2Parser.DEDENT) + self.state = 925 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__47: + self.state = 924 + self.behaviorWithDeclaration() + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class CompositionOperatorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_compositionOperator + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCompositionOperator"): + listener.enterCompositionOperator(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCompositionOperator"): + listener.exitCompositionOperator(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCompositionOperator"): + return visitor.visitCompositionOperator(self) + else: + return visitor.visitChildren(self) + + def compositionOperator(self): + + localctx = OpenSCENARIO2Parser.CompositionOperatorContext(self, self._ctx, self.state) + self.enterRule(localctx, 182, self.RULE_compositionOperator) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 927 + _la = self._input.LA(1) + if not ((((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << OpenSCENARIO2Parser.T__54) | (1 << OpenSCENARIO2Parser.T__55) | (1 << OpenSCENARIO2Parser.T__56))) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class BehaviorInvocationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def behaviorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorNameContext, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def behaviorWithDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorWithDeclarationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def actorExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorExpressionContext, 0) + + def argumentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_behaviorInvocation + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBehaviorInvocation"): + listener.enterBehaviorInvocation(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBehaviorInvocation"): + listener.exitBehaviorInvocation(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBehaviorInvocation"): + return visitor.visitBehaviorInvocation(self) + else: + return visitor.visitChildren(self) + + def behaviorInvocation(self): + + localctx = OpenSCENARIO2Parser.BehaviorInvocationContext(self, self._ctx, self.state) + self.enterRule(localctx, 184, self.RULE_behaviorInvocation) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 932 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 84, self._ctx) + if la_ == 1: + self.state = 929 + self.actorExpression() + self.state = 930 + self.match(OpenSCENARIO2Parser.T__1) + + self.state = 934 + self.behaviorName() + self.state = 935 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 937 + self._errHandler.sync(self) + _la = self._input.LA(1) + if ((((_la - 70)) & ~0x3f) == 0 and ((1 << (_la - 70)) & ((1 << (OpenSCENARIO2Parser.T__69 - 70)) | (1 << (OpenSCENARIO2Parser.T__74 - 70)) | (1 << (OpenSCENARIO2Parser.T__82 - 70)) | (1 << (OpenSCENARIO2Parser.T__86 - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_BRACK - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_PAREN - 70)) | (1 << (OpenSCENARIO2Parser.StringLiteral - 70)) | (1 << (OpenSCENARIO2Parser.FloatLiteral - 70)) | (1 << (OpenSCENARIO2Parser.UintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.HexUintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.IntLiteral - 70)) | (1 << (OpenSCENARIO2Parser.BoolLiteral - 70)) | (1 << (OpenSCENARIO2Parser.Identifier - 70)))) != 0): + self.state = 936 + self.argumentList() + + self.state = 939 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + self.state = 942 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__47]: + self.state = 940 + self.behaviorWithDeclaration() + pass + elif token in [OpenSCENARIO2Parser.NEWLINE]: + self.state = 941 + self.match(OpenSCENARIO2Parser.NEWLINE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class BehaviorWithDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def INDENT(self): + return self.getToken(OpenSCENARIO2Parser.INDENT, 0) + + def DEDENT(self): + return self.getToken(OpenSCENARIO2Parser.DEDENT, 0) + + def behaviorWithMember(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.BehaviorWithMemberContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.BehaviorWithMemberContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_behaviorWithDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBehaviorWithDeclaration"): + listener.enterBehaviorWithDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBehaviorWithDeclaration"): + listener.exitBehaviorWithDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBehaviorWithDeclaration"): + return visitor.visitBehaviorWithDeclaration(self) + else: + return visitor.visitChildren(self) + + def behaviorWithDeclaration(self): + + localctx = OpenSCENARIO2Parser.BehaviorWithDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 186, self.RULE_behaviorWithDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 944 + self.match(OpenSCENARIO2Parser.T__47) + self.state = 945 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 946 + self.match(OpenSCENARIO2Parser.NEWLINE) + self.state = 947 + self.match(OpenSCENARIO2Parser.INDENT) + self.state = 949 + self._errHandler.sync(self) + _la = self._input.LA(1) + while True: + self.state = 948 + self.behaviorWithMember() + self.state = 951 + self._errHandler.sync(self) + _la = self._input.LA(1) + if not (((((_la - 49)) & ~0x3f) == 0 and ((1 << (_la - 49)) & ((1 << (OpenSCENARIO2Parser.T__48 - 49)) | (1 << (OpenSCENARIO2Parser.T__51 - 49)) | (1 << (OpenSCENARIO2Parser.T__60 - 49)) | (1 << (OpenSCENARIO2Parser.Identifier - 49)))) != 0)): + break + + self.state = 953 + self.match(OpenSCENARIO2Parser.DEDENT) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class BehaviorWithMemberContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def constraintDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ConstraintDeclarationContext, 0) + + def modifierInvocation(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ModifierInvocationContext, 0) + + def untilDirective(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.UntilDirectiveContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_behaviorWithMember + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterBehaviorWithMember"): + listener.enterBehaviorWithMember(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitBehaviorWithMember"): + listener.exitBehaviorWithMember(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitBehaviorWithMember"): + return visitor.visitBehaviorWithMember(self) + else: + return visitor.visitChildren(self) + + def behaviorWithMember(self): + + localctx = OpenSCENARIO2Parser.BehaviorWithMemberContext(self, self._ctx, self.state) + self.enterRule(localctx, 188, self.RULE_behaviorWithMember) + try: + self.state = 958 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__48, OpenSCENARIO2Parser.T__51]: + self.enterOuterAlt(localctx, 1) + self.state = 955 + self.constraintDeclaration() + pass + elif token in [OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 2) + self.state = 956 + self.modifierInvocation() + pass + elif token in [OpenSCENARIO2Parser.T__60]: + self.enterOuterAlt(localctx, 3) + self.state = 957 + self.untilDirective() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class LabelNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_labelName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterLabelName"): + listener.enterLabelName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitLabelName"): + listener.exitLabelName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitLabelName"): + return visitor.visitLabelName(self) + else: + return visitor.visitChildren(self) + + def labelName(self): + + localctx = OpenSCENARIO2Parser.LabelNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 190, self.RULE_labelName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 960 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ActorExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def actorName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ActorNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_actorExpression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterActorExpression"): + listener.enterActorExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitActorExpression"): + listener.exitActorExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitActorExpression"): + return visitor.visitActorExpression(self) + else: + return visitor.visitChildren(self) + + def actorExpression(self): + + localctx = OpenSCENARIO2Parser.ActorExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 192, self.RULE_actorExpression) + try: + self.enterOuterAlt(localctx, 1) + self.state = 962 + self.actorName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class WaitDirectiveContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventSpecification(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventSpecificationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_waitDirective + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterWaitDirective"): + listener.enterWaitDirective(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitWaitDirective"): + listener.exitWaitDirective(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitWaitDirective"): + return visitor.visitWaitDirective(self) + else: + return visitor.visitChildren(self) + + def waitDirective(self): + + localctx = OpenSCENARIO2Parser.WaitDirectiveContext(self, self._ctx, self.state) + self.enterRule(localctx, 194, self.RULE_waitDirective) + try: + self.enterOuterAlt(localctx, 1) + self.state = 964 + self.match(OpenSCENARIO2Parser.T__57) + self.state = 965 + self.eventSpecification() + self.state = 966 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class EmitDirectiveContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventNameContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def argumentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_emitDirective + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterEmitDirective"): + listener.enterEmitDirective(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitEmitDirective"): + listener.exitEmitDirective(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitEmitDirective"): + return visitor.visitEmitDirective(self) + else: + return visitor.visitChildren(self) + + def emitDirective(self): + + localctx = OpenSCENARIO2Parser.EmitDirectiveContext(self, self._ctx, self.state) + self.enterRule(localctx, 196, self.RULE_emitDirective) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 968 + self.match(OpenSCENARIO2Parser.T__58) + self.state = 969 + self.eventName() + self.state = 974 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.OPEN_PAREN: + self.state = 970 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 971 + self.argumentList() + self.state = 972 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + + self.state = 976 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class CallDirectiveContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def methodInvocation(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.MethodInvocationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_callDirective + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCallDirective"): + listener.enterCallDirective(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCallDirective"): + listener.exitCallDirective(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCallDirective"): + return visitor.visitCallDirective(self) + else: + return visitor.visitChildren(self) + + def callDirective(self): + + localctx = OpenSCENARIO2Parser.CallDirectiveContext(self, self._ctx, self.state) + self.enterRule(localctx, 198, self.RULE_callDirective) + try: + self.enterOuterAlt(localctx, 1) + self.state = 978 + self.match(OpenSCENARIO2Parser.T__59) + self.state = 979 + self.methodInvocation() + self.state = 980 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class UntilDirectiveContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def eventSpecification(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventSpecificationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_untilDirective + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterUntilDirective"): + listener.enterUntilDirective(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitUntilDirective"): + listener.exitUntilDirective(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitUntilDirective"): + return visitor.visitUntilDirective(self) + else: + return visitor.visitChildren(self) + + def untilDirective(self): + + localctx = OpenSCENARIO2Parser.UntilDirectiveContext(self, self._ctx, self.state) + self.enterRule(localctx, 200, self.RULE_untilDirective) + try: + self.enterOuterAlt(localctx, 1) + self.state = 982 + self.match(OpenSCENARIO2Parser.T__60) + self.state = 983 + self.eventSpecification() + self.state = 984 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class MethodInvocationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def postfixExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PostfixExpContext, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def argumentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_methodInvocation + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMethodInvocation"): + listener.enterMethodInvocation(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMethodInvocation"): + listener.exitMethodInvocation(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMethodInvocation"): + return visitor.visitMethodInvocation(self) + else: + return visitor.visitChildren(self) + + def methodInvocation(self): + + localctx = OpenSCENARIO2Parser.MethodInvocationContext(self, self._ctx, self.state) + self.enterRule(localctx, 202, self.RULE_methodInvocation) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 986 + self.postfixExp(0) + self.state = 987 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 989 + self._errHandler.sync(self) + _la = self._input.LA(1) + if ((((_la - 70)) & ~0x3f) == 0 and ((1 << (_la - 70)) & ((1 << (OpenSCENARIO2Parser.T__69 - 70)) | (1 << (OpenSCENARIO2Parser.T__74 - 70)) | (1 << (OpenSCENARIO2Parser.T__82 - 70)) | (1 << (OpenSCENARIO2Parser.T__86 - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_BRACK - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_PAREN - 70)) | (1 << (OpenSCENARIO2Parser.StringLiteral - 70)) | (1 << (OpenSCENARIO2Parser.FloatLiteral - 70)) | (1 << (OpenSCENARIO2Parser.UintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.HexUintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.IntLiteral - 70)) | (1 << (OpenSCENARIO2Parser.BoolLiteral - 70)) | (1 << (OpenSCENARIO2Parser.Identifier - 70)))) != 0): + self.state = 988 + self.argumentList() + + self.state = 991 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class MethodDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def methodName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.MethodNameContext, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def methodImplementation(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.MethodImplementationContext, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def argumentListSpecification(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListSpecificationContext, 0) + + def returnType(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ReturnTypeContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_methodDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMethodDeclaration"): + listener.enterMethodDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMethodDeclaration"): + listener.exitMethodDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMethodDeclaration"): + return visitor.visitMethodDeclaration(self) + else: + return visitor.visitChildren(self) + + def methodDeclaration(self): + + localctx = OpenSCENARIO2Parser.MethodDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 204, self.RULE_methodDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 993 + self.match(OpenSCENARIO2Parser.T__61) + self.state = 994 + self.methodName() + self.state = 995 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 997 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.Identifier: + self.state = 996 + self.argumentListSpecification() + + self.state = 999 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + self.state = 1002 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__62: + self.state = 1000 + self.match(OpenSCENARIO2Parser.T__62) + self.state = 1001 + self.returnType() + + self.state = 1004 + self.methodImplementation() + self.state = 1005 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ReturnTypeContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def typeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeDeclaratorContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_returnType + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterReturnType"): + listener.enterReturnType(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitReturnType"): + listener.exitReturnType(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitReturnType"): + return visitor.visitReturnType(self) + else: + return visitor.visitChildren(self) + + def returnType(self): + + localctx = OpenSCENARIO2Parser.ReturnTypeContext(self, self._ctx, self.state) + self.enterRule(localctx, 206, self.RULE_returnType) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1007 + self.typeDeclarator() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class MethodImplementationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def structuredIdentifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.StructuredIdentifierContext, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def methodQualifier(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.MethodQualifierContext, 0) + + def argumentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_methodImplementation + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMethodImplementation"): + listener.enterMethodImplementation(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMethodImplementation"): + listener.exitMethodImplementation(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMethodImplementation"): + return visitor.visitMethodImplementation(self) + else: + return visitor.visitChildren(self) + + def methodImplementation(self): + + localctx = OpenSCENARIO2Parser.MethodImplementationContext(self, self._ctx, self.state) + self.enterRule(localctx, 208, self.RULE_methodImplementation) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1009 + self.match(OpenSCENARIO2Parser.T__3) + self.state = 1011 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__66: + self.state = 1010 + self.methodQualifier() + + self.state = 1024 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__63]: + self.state = 1013 + self.match(OpenSCENARIO2Parser.T__63) + self.state = 1014 + self.expression() + pass + elif token in [OpenSCENARIO2Parser.T__64]: + self.state = 1015 + self.match(OpenSCENARIO2Parser.T__64) + pass + elif token in [OpenSCENARIO2Parser.T__65]: + self.state = 1016 + self.match(OpenSCENARIO2Parser.T__65) + self.state = 1017 + self.structuredIdentifier(0) + self.state = 1018 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 1020 + self._errHandler.sync(self) + _la = self._input.LA(1) + if ((((_la - 70)) & ~0x3f) == 0 and ((1 << (_la - 70)) & ((1 << (OpenSCENARIO2Parser.T__69 - 70)) | (1 << (OpenSCENARIO2Parser.T__74 - 70)) | (1 << (OpenSCENARIO2Parser.T__82 - 70)) | (1 << (OpenSCENARIO2Parser.T__86 - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_BRACK - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_PAREN - 70)) | (1 << (OpenSCENARIO2Parser.StringLiteral - 70)) | (1 << (OpenSCENARIO2Parser.FloatLiteral - 70)) | (1 << (OpenSCENARIO2Parser.UintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.HexUintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.IntLiteral - 70)) | (1 << (OpenSCENARIO2Parser.BoolLiteral - 70)) | (1 << (OpenSCENARIO2Parser.Identifier - 70)))) != 0): + self.state = 1019 + self.argumentList() + + self.state = 1022 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class MethodQualifierContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_methodQualifier + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMethodQualifier"): + listener.enterMethodQualifier(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMethodQualifier"): + listener.exitMethodQualifier(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMethodQualifier"): + return visitor.visitMethodQualifier(self) + else: + return visitor.visitChildren(self) + + def methodQualifier(self): + + localctx = OpenSCENARIO2Parser.MethodQualifierContext(self, self._ctx, self.state) + self.enterRule(localctx, 210, self.RULE_methodQualifier) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1026 + self.match(OpenSCENARIO2Parser.T__66) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class MethodNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_methodName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMethodName"): + listener.enterMethodName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMethodName"): + listener.exitMethodName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMethodName"): + return visitor.visitMethodName(self) + else: + return visitor.visitChildren(self) + + def methodName(self): + + localctx = OpenSCENARIO2Parser.MethodNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 212, self.RULE_methodName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1028 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class CoverageDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def coverDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.CoverDeclarationContext, 0) + + def recordDeclaration(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.RecordDeclarationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_coverageDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCoverageDeclaration"): + listener.enterCoverageDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCoverageDeclaration"): + listener.exitCoverageDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCoverageDeclaration"): + return visitor.visitCoverageDeclaration(self) + else: + return visitor.visitChildren(self) + + def coverageDeclaration(self): + + localctx = OpenSCENARIO2Parser.CoverageDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 214, self.RULE_coverageDeclaration) + try: + self.state = 1032 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__67]: + self.enterOuterAlt(localctx, 1) + self.state = 1030 + self.coverDeclaration() + pass + elif token in [OpenSCENARIO2Parser.T__68]: + self.enterOuterAlt(localctx, 2) + self.state = 1031 + self.recordDeclaration() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class CoverDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def targetName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TargetNameContext, 0) + + def coverageArgumentList(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.CoverageArgumentListContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.CoverageArgumentListContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_coverDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCoverDeclaration"): + listener.enterCoverDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCoverDeclaration"): + listener.exitCoverDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCoverDeclaration"): + return visitor.visitCoverDeclaration(self) + else: + return visitor.visitChildren(self) + + def coverDeclaration(self): + + localctx = OpenSCENARIO2Parser.CoverDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 216, self.RULE_coverDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1034 + self.match(OpenSCENARIO2Parser.T__67) + self.state = 1035 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 1037 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.Identifier: + self.state = 1036 + self.targetName() + + self.state = 1042 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 1039 + self.coverageArgumentList() + self.state = 1044 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 1045 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + self.state = 1046 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class RecordDeclarationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def NEWLINE(self): + return self.getToken(OpenSCENARIO2Parser.NEWLINE, 0) + + def targetName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TargetNameContext, 0) + + def coverageArgumentList(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.CoverageArgumentListContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.CoverageArgumentListContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_recordDeclaration + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterRecordDeclaration"): + listener.enterRecordDeclaration(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitRecordDeclaration"): + listener.exitRecordDeclaration(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitRecordDeclaration"): + return visitor.visitRecordDeclaration(self) + else: + return visitor.visitChildren(self) + + def recordDeclaration(self): + + localctx = OpenSCENARIO2Parser.RecordDeclarationContext(self, self._ctx, self.state) + self.enterRule(localctx, 218, self.RULE_recordDeclaration) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1048 + self.match(OpenSCENARIO2Parser.T__68) + self.state = 1049 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 1051 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.Identifier: + self.state = 1050 + self.targetName() + + self.state = 1056 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 1053 + self.coverageArgumentList() + self.state = 1058 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 1059 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + self.state = 1060 + self.match(OpenSCENARIO2Parser.NEWLINE) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class CoverageArgumentListContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_coverageArgumentList + + def copyFrom(self, ctx: ParserRuleContext): + super().copyFrom(ctx) + + class CoverageEventContext(CoverageArgumentListContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.CoverageArgumentListContext + super().__init__(parser) + self.copyFrom(ctx) + + def eventName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EventNameContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCoverageEvent"): + listener.enterCoverageEvent(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCoverageEvent"): + listener.exitCoverageEvent(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCoverageEvent"): + return visitor.visitCoverageEvent(self) + else: + return visitor.visitChildren(self) + + class CoverageEveryContext(CoverageArgumentListContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.CoverageArgumentListContext + super().__init__(parser) + self.copyFrom(ctx) + + def valueExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ValueExpContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCoverageEvery"): + listener.enterCoverageEvery(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCoverageEvery"): + listener.exitCoverageEvery(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCoverageEvery"): + return visitor.visitCoverageEvery(self) + else: + return visitor.visitChildren(self) + + class CoverageNameArgumentContext(CoverageArgumentListContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.CoverageArgumentListContext + super().__init__(parser) + self.copyFrom(ctx) + + def namedArgument(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.NamedArgumentContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCoverageNameArgument"): + listener.enterCoverageNameArgument(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCoverageNameArgument"): + listener.exitCoverageNameArgument(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCoverageNameArgument"): + return visitor.visitCoverageNameArgument(self) + else: + return visitor.visitChildren(self) + + class CoverageExpressionContext(CoverageArgumentListContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.CoverageArgumentListContext + super().__init__(parser) + self.copyFrom(ctx) + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCoverageExpression"): + listener.enterCoverageExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCoverageExpression"): + listener.exitCoverageExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCoverageExpression"): + return visitor.visitCoverageExpression(self) + else: + return visitor.visitChildren(self) + + class CoverageRangeContext(CoverageArgumentListContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.CoverageArgumentListContext + super().__init__(parser) + self.copyFrom(ctx) + + def rangeConstructor(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.RangeConstructorContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCoverageRange"): + listener.enterCoverageRange(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCoverageRange"): + listener.exitCoverageRange(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCoverageRange"): + return visitor.visitCoverageRange(self) + else: + return visitor.visitChildren(self) + + class CoverageUnitContext(CoverageArgumentListContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.CoverageArgumentListContext + super().__init__(parser) + self.copyFrom(ctx) + + def unitName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.UnitNameContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCoverageUnit"): + listener.enterCoverageUnit(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCoverageUnit"): + listener.exitCoverageUnit(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCoverageUnit"): + return visitor.visitCoverageUnit(self) + else: + return visitor.visitChildren(self) + + def coverageArgumentList(self): + + localctx = OpenSCENARIO2Parser.CoverageArgumentListContext(self, self._ctx, self.state) + self.enterRule(localctx, 220, self.RULE_coverageArgumentList) + try: + self.state = 1084 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 101, self._ctx) + if la_ == 1: + localctx = OpenSCENARIO2Parser.CoverageExpressionContext(self, localctx) + self.enterOuterAlt(localctx, 1) + self.state = 1062 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1063 + self.match(OpenSCENARIO2Parser.T__63) + self.state = 1064 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 1065 + self.expression() + pass + + elif la_ == 2: + localctx = OpenSCENARIO2Parser.CoverageUnitContext(self, localctx) + self.enterOuterAlt(localctx, 2) + self.state = 1066 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1067 + self.match(OpenSCENARIO2Parser.T__5) + self.state = 1068 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 1069 + self.unitName() + pass + + elif la_ == 3: + localctx = OpenSCENARIO2Parser.CoverageRangeContext(self, localctx) + self.enterOuterAlt(localctx, 3) + self.state = 1070 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1071 + self.match(OpenSCENARIO2Parser.T__69) + self.state = 1072 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 1073 + self.rangeConstructor() + pass + + elif la_ == 4: + localctx = OpenSCENARIO2Parser.CoverageEveryContext(self, localctx) + self.enterOuterAlt(localctx, 4) + self.state = 1074 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1075 + self.match(OpenSCENARIO2Parser.T__44) + self.state = 1076 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 1077 + self.valueExp() + pass + + elif la_ == 5: + localctx = OpenSCENARIO2Parser.CoverageEventContext(self, localctx) + self.enterOuterAlt(localctx, 5) + self.state = 1078 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1079 + self.match(OpenSCENARIO2Parser.T__37) + self.state = 1080 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 1081 + self.eventName() + pass + + elif la_ == 6: + localctx = OpenSCENARIO2Parser.CoverageNameArgumentContext(self, localctx) + self.enterOuterAlt(localctx, 6) + self.state = 1082 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1083 + self.namedArgument() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class TargetNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_targetName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTargetName"): + listener.enterTargetName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTargetName"): + listener.exitTargetName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTargetName"): + return visitor.visitTargetName(self) + else: + return visitor.visitChildren(self) + + def targetName(self): + + localctx = OpenSCENARIO2Parser.TargetNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 222, self.RULE_targetName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1086 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def implication(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ImplicationContext, 0) + + def ternaryOpExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TernaryOpExpContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_expression + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterExpression"): + listener.enterExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitExpression"): + listener.exitExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitExpression"): + return visitor.visitExpression(self) + else: + return visitor.visitChildren(self) + + def expression(self): + + localctx = OpenSCENARIO2Parser.ExpressionContext(self, self._ctx, self.state) + self.enterRule(localctx, 224, self.RULE_expression) + try: + self.state = 1090 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 102, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 1088 + self.implication() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 1089 + self.ternaryOpExp() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class TernaryOpExpContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def implication(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ImplicationContext, 0) + + def expression(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ExpressionContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_ternaryOpExp + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTernaryOpExp"): + listener.enterTernaryOpExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTernaryOpExp"): + listener.exitTernaryOpExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTernaryOpExp"): + return visitor.visitTernaryOpExp(self) + else: + return visitor.visitChildren(self) + + def ternaryOpExp(self): + + localctx = OpenSCENARIO2Parser.TernaryOpExpContext(self, self._ctx, self.state) + self.enterRule(localctx, 226, self.RULE_ternaryOpExp) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1092 + self.implication() + self.state = 1093 + self.match(OpenSCENARIO2Parser.T__70) + self.state = 1094 + self.expression() + self.state = 1095 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 1096 + self.expression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ImplicationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def disjunction(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.DisjunctionContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.DisjunctionContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_implication + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterImplication"): + listener.enterImplication(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitImplication"): + listener.exitImplication(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitImplication"): + return visitor.visitImplication(self) + else: + return visitor.visitChildren(self) + + def implication(self): + + localctx = OpenSCENARIO2Parser.ImplicationContext(self, self._ctx, self.state) + self.enterRule(localctx, 228, self.RULE_implication) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1098 + self.disjunction() + self.state = 1103 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__71: + self.state = 1099 + self.match(OpenSCENARIO2Parser.T__71) + self.state = 1100 + self.disjunction() + self.state = 1105 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class DisjunctionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def conjunction(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ConjunctionContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ConjunctionContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_disjunction + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterDisjunction"): + listener.enterDisjunction(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitDisjunction"): + listener.exitDisjunction(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitDisjunction"): + return visitor.visitDisjunction(self) + else: + return visitor.visitChildren(self) + + def disjunction(self): + + localctx = OpenSCENARIO2Parser.DisjunctionContext(self, self._ctx, self.state) + self.enterRule(localctx, 230, self.RULE_disjunction) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1106 + self.conjunction() + self.state = 1111 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__72: + self.state = 1107 + self.match(OpenSCENARIO2Parser.T__72) + self.state = 1108 + self.conjunction() + self.state = 1113 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ConjunctionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def inversion(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.InversionContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.InversionContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_conjunction + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterConjunction"): + listener.enterConjunction(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitConjunction"): + listener.exitConjunction(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitConjunction"): + return visitor.visitConjunction(self) + else: + return visitor.visitChildren(self) + + def conjunction(self): + + localctx = OpenSCENARIO2Parser.ConjunctionContext(self, self._ctx, self.state) + self.enterRule(localctx, 232, self.RULE_conjunction) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1114 + self.inversion() + self.state = 1119 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__73: + self.state = 1115 + self.match(OpenSCENARIO2Parser.T__73) + self.state = 1116 + self.inversion() + self.state = 1121 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class InversionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def inversion(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.InversionContext, 0) + + def relation(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.RelationContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_inversion + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterInversion"): + listener.enterInversion(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitInversion"): + listener.exitInversion(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitInversion"): + return visitor.visitInversion(self) + else: + return visitor.visitChildren(self) + + def inversion(self): + + localctx = OpenSCENARIO2Parser.InversionContext(self, self._ctx, self.state) + self.enterRule(localctx, 234, self.RULE_inversion) + try: + self.state = 1125 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__74]: + self.enterOuterAlt(localctx, 1) + self.state = 1122 + self.match(OpenSCENARIO2Parser.T__74) + self.state = 1123 + self.inversion() + pass + elif token in [OpenSCENARIO2Parser.T__69, OpenSCENARIO2Parser.T__82, OpenSCENARIO2Parser.T__86, OpenSCENARIO2Parser.OPEN_BRACK, OpenSCENARIO2Parser.OPEN_PAREN, OpenSCENARIO2Parser.StringLiteral, OpenSCENARIO2Parser.FloatLiteral, OpenSCENARIO2Parser.UintLiteral, OpenSCENARIO2Parser.HexUintLiteral, OpenSCENARIO2Parser.IntLiteral, OpenSCENARIO2Parser.BoolLiteral, OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 2) + self.state = 1124 + self.relation(0) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class RelationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_relation + + def copyFrom(self, ctx: ParserRuleContext): + super().copyFrom(ctx) + + class RelationExpContext(RelationContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.RelationContext + super().__init__(parser) + self.copyFrom(ctx) + + def relation(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.RelationContext, 0) + + def relationalOp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.RelationalOpContext, 0) + + def sumExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SumExpressionContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterRelationExp"): + listener.enterRelationExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitRelationExp"): + listener.exitRelationExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitRelationExp"): + return visitor.visitRelationExp(self) + else: + return visitor.visitChildren(self) + + class SumExpContext(RelationContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.RelationContext + super().__init__(parser) + self.copyFrom(ctx) + + def sumExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SumExpressionContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterSumExp"): + listener.enterSumExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitSumExp"): + listener.exitSumExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitSumExp"): + return visitor.visitSumExp(self) + else: + return visitor.visitChildren(self) + + def relation(self, _p: int = 0): + _parentctx = self._ctx + _parentState = self.state + localctx = OpenSCENARIO2Parser.RelationContext(self, self._ctx, _parentState) + _prevctx = localctx + _startState = 236 + self.enterRecursionRule(localctx, 236, self.RULE_relation, _p) + try: + self.enterOuterAlt(localctx, 1) + localctx = OpenSCENARIO2Parser.SumExpContext(self, localctx) + self._ctx = localctx + _prevctx = localctx + + self.state = 1128 + self.sumExpression(0) + self._ctx.stop = self._input.LT(-1) + self.state = 1136 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 107, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + if self._parseListeners is not None: + self.triggerExitRuleEvent() + _prevctx = localctx + localctx = OpenSCENARIO2Parser.RelationExpContext( + self, OpenSCENARIO2Parser.RelationContext(self, _parentctx, _parentState)) + self.pushNewRecursionContext(localctx, _startState, self.RULE_relation) + self.state = 1130 + if not self.precpred(self._ctx, 1): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 1)") + self.state = 1131 + self.relationalOp() + self.state = 1132 + self.sumExpression(0) + self.state = 1138 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 107, self._ctx) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.unrollRecursionContexts(_parentctx) + return localctx + + class RelationalOpContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_relationalOp + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterRelationalOp"): + listener.enterRelationalOp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitRelationalOp"): + listener.exitRelationalOp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitRelationalOp"): + return visitor.visitRelationalOp(self) + else: + return visitor.visitChildren(self) + + def relationalOp(self): + + localctx = OpenSCENARIO2Parser.RelationalOpContext(self, self._ctx, self.state) + self.enterRule(localctx, 238, self.RULE_relationalOp) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1139 + _la = self._input.LA(1) + if not (((((_la - 23)) & ~0x3f) == 0 and ((1 << (_la - 23)) & ((1 << (OpenSCENARIO2Parser.T__22 - 23)) | (1 << (OpenSCENARIO2Parser.T__75 - 23)) | (1 << (OpenSCENARIO2Parser.T__76 - 23)) | (1 << (OpenSCENARIO2Parser.T__77 - 23)) | (1 << (OpenSCENARIO2Parser.T__78 - 23)) | (1 << (OpenSCENARIO2Parser.T__79 - 23)) | (1 << (OpenSCENARIO2Parser.T__80 - 23)))) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class SumExpressionContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_sumExpression + + def copyFrom(self, ctx: ParserRuleContext): + super().copyFrom(ctx) + + class TermExpContext(SumExpressionContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.SumExpressionContext + super().__init__(parser) + self.copyFrom(ctx) + + def term(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TermContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTermExp"): + listener.enterTermExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTermExp"): + listener.exitTermExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTermExp"): + return visitor.visitTermExp(self) + else: + return visitor.visitChildren(self) + + class AdditiveExpContext(SumExpressionContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.SumExpressionContext + super().__init__(parser) + self.copyFrom(ctx) + + def sumExpression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.SumExpressionContext, 0) + + def additiveOp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.AdditiveOpContext, 0) + + def term(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TermContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterAdditiveExp"): + listener.enterAdditiveExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitAdditiveExp"): + listener.exitAdditiveExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitAdditiveExp"): + return visitor.visitAdditiveExp(self) + else: + return visitor.visitChildren(self) + + def sumExpression(self, _p: int = 0): + _parentctx = self._ctx + _parentState = self.state + localctx = OpenSCENARIO2Parser.SumExpressionContext(self, self._ctx, _parentState) + _prevctx = localctx + _startState = 240 + self.enterRecursionRule(localctx, 240, self.RULE_sumExpression, _p) + try: + self.enterOuterAlt(localctx, 1) + localctx = OpenSCENARIO2Parser.TermExpContext(self, localctx) + self._ctx = localctx + _prevctx = localctx + + self.state = 1142 + self.term(0) + self._ctx.stop = self._input.LT(-1) + self.state = 1150 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 108, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + if self._parseListeners is not None: + self.triggerExitRuleEvent() + _prevctx = localctx + localctx = OpenSCENARIO2Parser.AdditiveExpContext( + self, OpenSCENARIO2Parser.SumExpressionContext(self, _parentctx, _parentState)) + self.pushNewRecursionContext(localctx, _startState, self.RULE_sumExpression) + self.state = 1144 + if not self.precpred(self._ctx, 1): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 1)") + self.state = 1145 + self.additiveOp() + self.state = 1146 + self.term(0) + self.state = 1152 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 108, self._ctx) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.unrollRecursionContexts(_parentctx) + return localctx + + class AdditiveOpContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_additiveOp + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterAdditiveOp"): + listener.enterAdditiveOp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitAdditiveOp"): + listener.exitAdditiveOp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitAdditiveOp"): + return visitor.visitAdditiveOp(self) + else: + return visitor.visitChildren(self) + + def additiveOp(self): + + localctx = OpenSCENARIO2Parser.AdditiveOpContext(self, self._ctx, self.state) + self.enterRule(localctx, 242, self.RULE_additiveOp) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1153 + _la = self._input.LA(1) + if not (_la == OpenSCENARIO2Parser.T__81 or _la == OpenSCENARIO2Parser.T__82): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class TermContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_term + + def copyFrom(self, ctx: ParserRuleContext): + super().copyFrom(ctx) + + class MultiplicativeExpContext(TermContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.TermContext + super().__init__(parser) + self.copyFrom(ctx) + + def term(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TermContext, 0) + + def multiplicativeOp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.MultiplicativeOpContext, 0) + + def factor(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FactorContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMultiplicativeExp"): + listener.enterMultiplicativeExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMultiplicativeExp"): + listener.exitMultiplicativeExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMultiplicativeExp"): + return visitor.visitMultiplicativeExp(self) + else: + return visitor.visitChildren(self) + + class FactorExpContext(TermContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.TermContext + super().__init__(parser) + self.copyFrom(ctx) + + def factor(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FactorContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFactorExp"): + listener.enterFactorExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFactorExp"): + listener.exitFactorExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFactorExp"): + return visitor.visitFactorExp(self) + else: + return visitor.visitChildren(self) + + def term(self, _p: int = 0): + _parentctx = self._ctx + _parentState = self.state + localctx = OpenSCENARIO2Parser.TermContext(self, self._ctx, _parentState) + _prevctx = localctx + _startState = 244 + self.enterRecursionRule(localctx, 244, self.RULE_term, _p) + try: + self.enterOuterAlt(localctx, 1) + localctx = OpenSCENARIO2Parser.FactorExpContext(self, localctx) + self._ctx = localctx + _prevctx = localctx + + self.state = 1156 + self.factor() + self._ctx.stop = self._input.LT(-1) + self.state = 1164 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 109, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + if self._parseListeners is not None: + self.triggerExitRuleEvent() + _prevctx = localctx + localctx = OpenSCENARIO2Parser.MultiplicativeExpContext( + self, OpenSCENARIO2Parser.TermContext(self, _parentctx, _parentState)) + self.pushNewRecursionContext(localctx, _startState, self.RULE_term) + self.state = 1158 + if not self.precpred(self._ctx, 1): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 1)") + self.state = 1159 + self.multiplicativeOp() + self.state = 1160 + self.factor() + self.state = 1166 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 109, self._ctx) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.unrollRecursionContexts(_parentctx) + return localctx + + class MultiplicativeOpContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_multiplicativeOp + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterMultiplicativeOp"): + listener.enterMultiplicativeOp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitMultiplicativeOp"): + listener.exitMultiplicativeOp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitMultiplicativeOp"): + return visitor.visitMultiplicativeOp(self) + else: + return visitor.visitChildren(self) + + def multiplicativeOp(self): + + localctx = OpenSCENARIO2Parser.MultiplicativeOpContext(self, self._ctx, self.state) + self.enterRule(localctx, 246, self.RULE_multiplicativeOp) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1167 + _la = self._input.LA(1) + if not (((((_la - 84)) & ~0x3f) == 0 and ((1 << (_la - 84)) & ((1 << (OpenSCENARIO2Parser.T__83 - 84)) | (1 << (OpenSCENARIO2Parser.T__84 - 84)) | (1 << (OpenSCENARIO2Parser.T__85 - 84)))) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class FactorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def postfixExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PostfixExpContext, 0) + + def factor(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FactorContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_factor + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFactor"): + listener.enterFactor(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFactor"): + listener.exitFactor(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFactor"): + return visitor.visitFactor(self) + else: + return visitor.visitChildren(self) + + def factor(self): + + localctx = OpenSCENARIO2Parser.FactorContext(self, self._ctx, self.state) + self.enterRule(localctx, 248, self.RULE_factor) + try: + self.state = 1172 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__69, OpenSCENARIO2Parser.T__86, OpenSCENARIO2Parser.OPEN_BRACK, OpenSCENARIO2Parser.OPEN_PAREN, OpenSCENARIO2Parser.StringLiteral, OpenSCENARIO2Parser.FloatLiteral, OpenSCENARIO2Parser.UintLiteral, OpenSCENARIO2Parser.HexUintLiteral, OpenSCENARIO2Parser.IntLiteral, OpenSCENARIO2Parser.BoolLiteral, OpenSCENARIO2Parser.Identifier]: + self.enterOuterAlt(localctx, 1) + self.state = 1169 + self.postfixExp(0) + pass + elif token in [OpenSCENARIO2Parser.T__82]: + self.enterOuterAlt(localctx, 2) + self.state = 1170 + self.match(OpenSCENARIO2Parser.T__82) + self.state = 1171 + self.factor() + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class PostfixExpContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_postfixExp + + def copyFrom(self, ctx: ParserRuleContext): + super().copyFrom(ctx) + + class PrimaryExpressionContext(PostfixExpContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.PostfixExpContext + super().__init__(parser) + self.copyFrom(ctx) + + def primaryExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PrimaryExpContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPrimaryExpression"): + listener.enterPrimaryExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPrimaryExpression"): + listener.exitPrimaryExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPrimaryExpression"): + return visitor.visitPrimaryExpression(self) + else: + return visitor.visitChildren(self) + + class CastExpressionContext(PostfixExpContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.PostfixExpContext + super().__init__(parser) + self.copyFrom(ctx) + + def postfixExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PostfixExpContext, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def typeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeDeclaratorContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterCastExpression"): + listener.enterCastExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitCastExpression"): + listener.exitCastExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitCastExpression"): + return visitor.visitCastExpression(self) + else: + return visitor.visitChildren(self) + + class FunctionApplicationExpressionContext(PostfixExpContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.PostfixExpContext + super().__init__(parser) + self.copyFrom(ctx) + + def postfixExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PostfixExpContext, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def argumentList(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentListContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFunctionApplicationExpression"): + listener.enterFunctionApplicationExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFunctionApplicationExpression"): + listener.exitFunctionApplicationExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFunctionApplicationExpression"): + return visitor.visitFunctionApplicationExpression(self) + else: + return visitor.visitChildren(self) + + class FieldAccessExpressionContext(PostfixExpContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.PostfixExpContext + super().__init__(parser) + self.copyFrom(ctx) + + def postfixExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PostfixExpContext, 0) + + def fieldName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldNameContext, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFieldAccessExpression"): + listener.enterFieldAccessExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFieldAccessExpression"): + listener.exitFieldAccessExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFieldAccessExpression"): + return visitor.visitFieldAccessExpression(self) + else: + return visitor.visitChildren(self) + + class ElementAccessExpressionContext(PostfixExpContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.PostfixExpContext + super().__init__(parser) + self.copyFrom(ctx) + + def postfixExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PostfixExpContext, 0) + + def OPEN_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_BRACK, 0) + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def CLOSE_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_BRACK, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterElementAccessExpression"): + listener.enterElementAccessExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitElementAccessExpression"): + listener.exitElementAccessExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitElementAccessExpression"): + return visitor.visitElementAccessExpression(self) + else: + return visitor.visitChildren(self) + + class TypeTestExpressionContext(PostfixExpContext): + + def __init__(self, parser, ctx: ParserRuleContext): # actually a OpenSCENARIO2Parser.PostfixExpContext + super().__init__(parser) + self.copyFrom(ctx) + + def postfixExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PostfixExpContext, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def typeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeDeclaratorContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterTypeTestExpression"): + listener.enterTypeTestExpression(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitTypeTestExpression"): + listener.exitTypeTestExpression(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitTypeTestExpression"): + return visitor.visitTypeTestExpression(self) + else: + return visitor.visitChildren(self) + + def postfixExp(self, _p: int = 0): + _parentctx = self._ctx + _parentState = self.state + localctx = OpenSCENARIO2Parser.PostfixExpContext(self, self._ctx, _parentState) + _prevctx = localctx + _startState = 250 + self.enterRecursionRule(localctx, 250, self.RULE_postfixExp, _p) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + localctx = OpenSCENARIO2Parser.PrimaryExpressionContext(self, localctx) + self._ctx = localctx + _prevctx = localctx + + self.state = 1175 + self.primaryExp() + self._ctx.stop = self._input.LT(-1) + self.state = 1207 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 113, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + if self._parseListeners is not None: + self.triggerExitRuleEvent() + _prevctx = localctx + self.state = 1205 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 112, self._ctx) + if la_ == 1: + localctx = OpenSCENARIO2Parser.CastExpressionContext( + self, OpenSCENARIO2Parser.PostfixExpContext(self, _parentctx, _parentState)) + self.pushNewRecursionContext(localctx, _startState, self.RULE_postfixExp) + self.state = 1177 + if not self.precpred(self._ctx, 5): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 5)") + self.state = 1178 + self.match(OpenSCENARIO2Parser.T__1) + self.state = 1179 + self.match(OpenSCENARIO2Parser.T__40) + self.state = 1180 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 1181 + self.typeDeclarator() + self.state = 1182 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + pass + + elif la_ == 2: + localctx = OpenSCENARIO2Parser.TypeTestExpressionContext( + self, OpenSCENARIO2Parser.PostfixExpContext(self, _parentctx, _parentState)) + self.pushNewRecursionContext(localctx, _startState, self.RULE_postfixExp) + self.state = 1184 + if not self.precpred(self._ctx, 4): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 4)") + self.state = 1185 + self.match(OpenSCENARIO2Parser.T__1) + self.state = 1186 + self.match(OpenSCENARIO2Parser.T__3) + self.state = 1187 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 1188 + self.typeDeclarator() + self.state = 1189 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + pass + + elif la_ == 3: + localctx = OpenSCENARIO2Parser.ElementAccessExpressionContext( + self, OpenSCENARIO2Parser.PostfixExpContext(self, _parentctx, _parentState)) + self.pushNewRecursionContext(localctx, _startState, self.RULE_postfixExp) + self.state = 1191 + if not self.precpred(self._ctx, 3): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 3)") + self.state = 1192 + self.match(OpenSCENARIO2Parser.OPEN_BRACK) + self.state = 1193 + self.expression() + self.state = 1194 + self.match(OpenSCENARIO2Parser.CLOSE_BRACK) + pass + + elif la_ == 4: + localctx = OpenSCENARIO2Parser.FunctionApplicationExpressionContext( + self, OpenSCENARIO2Parser.PostfixExpContext(self, _parentctx, _parentState)) + self.pushNewRecursionContext(localctx, _startState, self.RULE_postfixExp) + self.state = 1196 + if not self.precpred(self._ctx, 2): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 2)") + self.state = 1197 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 1199 + self._errHandler.sync(self) + _la = self._input.LA(1) + if ((((_la - 70)) & ~0x3f) == 0 and ((1 << (_la - 70)) & ((1 << (OpenSCENARIO2Parser.T__69 - 70)) | (1 << (OpenSCENARIO2Parser.T__74 - 70)) | (1 << (OpenSCENARIO2Parser.T__82 - 70)) | (1 << (OpenSCENARIO2Parser.T__86 - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_BRACK - 70)) | (1 << (OpenSCENARIO2Parser.OPEN_PAREN - 70)) | (1 << (OpenSCENARIO2Parser.StringLiteral - 70)) | (1 << (OpenSCENARIO2Parser.FloatLiteral - 70)) | (1 << (OpenSCENARIO2Parser.UintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.HexUintLiteral - 70)) | (1 << (OpenSCENARIO2Parser.IntLiteral - 70)) | (1 << (OpenSCENARIO2Parser.BoolLiteral - 70)) | (1 << (OpenSCENARIO2Parser.Identifier - 70)))) != 0): + self.state = 1198 + self.argumentList() + + self.state = 1201 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + pass + + elif la_ == 5: + localctx = OpenSCENARIO2Parser.FieldAccessExpressionContext( + self, OpenSCENARIO2Parser.PostfixExpContext(self, _parentctx, _parentState)) + self.pushNewRecursionContext(localctx, _startState, self.RULE_postfixExp) + self.state = 1202 + if not self.precpred(self._ctx, 1): + from antlr4.error.Errors import FailedPredicateException + raise FailedPredicateException(self, "self.precpred(self._ctx, 1)") + self.state = 1203 + self.match(OpenSCENARIO2Parser.T__1) + self.state = 1204 + self.fieldName() + pass + + self.state = 1209 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 113, self._ctx) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.unrollRecursionContexts(_parentctx) + return localctx + + class FieldAccessContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def postfixExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PostfixExpContext, 0) + + def fieldName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldNameContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_fieldAccess + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterFieldAccess"): + listener.enterFieldAccess(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitFieldAccess"): + listener.exitFieldAccess(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitFieldAccess"): + return visitor.visitFieldAccess(self) + else: + return visitor.visitChildren(self) + + def fieldAccess(self): + + localctx = OpenSCENARIO2Parser.FieldAccessContext(self, self._ctx, self.state) + self.enterRule(localctx, 252, self.RULE_fieldAccess) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1210 + self.postfixExp(0) + self.state = 1211 + self.match(OpenSCENARIO2Parser.T__1) + self.state = 1212 + self.fieldName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class PrimaryExpContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def valueExp(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ValueExpContext, 0) + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_primaryExp + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPrimaryExp"): + listener.enterPrimaryExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPrimaryExp"): + listener.exitPrimaryExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPrimaryExp"): + return visitor.visitPrimaryExp(self) + else: + return visitor.visitChildren(self) + + def primaryExp(self): + + localctx = OpenSCENARIO2Parser.PrimaryExpContext(self, self._ctx, self.state) + self.enterRule(localctx, 254, self.RULE_primaryExp) + try: + self.state = 1221 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 114, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 1214 + self.valueExp() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 1215 + self.match(OpenSCENARIO2Parser.T__86) + pass + + elif la_ == 3: + self.enterOuterAlt(localctx, 3) + self.state = 1216 + self.match(OpenSCENARIO2Parser.Identifier) + pass + + elif la_ == 4: + self.enterOuterAlt(localctx, 4) + self.state = 1217 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 1218 + self.expression() + self.state = 1219 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ValueExpContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def physicalLiteral(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.PhysicalLiteralContext, 0) + + def FloatLiteral(self): + return self.getToken(OpenSCENARIO2Parser.FloatLiteral, 0) + + def integerLiteral(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.IntegerLiteralContext, 0) + + def BoolLiteral(self): + return self.getToken(OpenSCENARIO2Parser.BoolLiteral, 0) + + def StringLiteral(self): + return self.getToken(OpenSCENARIO2Parser.StringLiteral, 0) + + def identifierReference(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.IdentifierReferenceContext, 0) + + def enumValueReference(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.EnumValueReferenceContext, 0) + + def listConstructor(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ListConstructorContext, 0) + + def rangeConstructor(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.RangeConstructorContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_valueExp + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterValueExp"): + listener.enterValueExp(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitValueExp"): + listener.exitValueExp(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitValueExp"): + return visitor.visitValueExp(self) + else: + return visitor.visitChildren(self) + + def valueExp(self): + + localctx = OpenSCENARIO2Parser.ValueExpContext(self, self._ctx, self.state) + self.enterRule(localctx, 256, self.RULE_valueExp) + try: + self.state = 1232 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 115, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 1223 + self.physicalLiteral() + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 1224 + self.match(OpenSCENARIO2Parser.FloatLiteral) + pass + + elif la_ == 3: + self.enterOuterAlt(localctx, 3) + self.state = 1225 + self.integerLiteral() + pass + + elif la_ == 4: + self.enterOuterAlt(localctx, 4) + self.state = 1226 + self.match(OpenSCENARIO2Parser.BoolLiteral) + pass + + elif la_ == 5: + self.enterOuterAlt(localctx, 5) + self.state = 1227 + self.match(OpenSCENARIO2Parser.StringLiteral) + pass + + elif la_ == 6: + self.enterOuterAlt(localctx, 6) + self.state = 1228 + self.identifierReference() + pass + + elif la_ == 7: + self.enterOuterAlt(localctx, 7) + self.state = 1229 + self.enumValueReference() + pass + + elif la_ == 8: + self.enterOuterAlt(localctx, 8) + self.state = 1230 + self.listConstructor() + pass + + elif la_ == 9: + self.enterOuterAlt(localctx, 9) + self.state = 1231 + self.rangeConstructor() + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ListConstructorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_BRACK, 0) + + def expression(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ExpressionContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, i) + + def CLOSE_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_BRACK, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_listConstructor + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterListConstructor"): + listener.enterListConstructor(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitListConstructor"): + listener.exitListConstructor(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitListConstructor"): + return visitor.visitListConstructor(self) + else: + return visitor.visitChildren(self) + + def listConstructor(self): + + localctx = OpenSCENARIO2Parser.ListConstructorContext(self, self._ctx, self.state) + self.enterRule(localctx, 258, self.RULE_listConstructor) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1234 + self.match(OpenSCENARIO2Parser.OPEN_BRACK) + self.state = 1235 + self.expression() + self.state = 1240 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 1236 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1237 + self.expression() + self.state = 1242 + self._errHandler.sync(self) + _la = self._input.LA(1) + + self.state = 1243 + self.match(OpenSCENARIO2Parser.CLOSE_BRACK) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class RangeConstructorContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def OPEN_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_PAREN, 0) + + def expression(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ExpressionContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, i) + + def CLOSE_PAREN(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_PAREN, 0) + + def OPEN_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.OPEN_BRACK, 0) + + def CLOSE_BRACK(self): + return self.getToken(OpenSCENARIO2Parser.CLOSE_BRACK, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_rangeConstructor + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterRangeConstructor"): + listener.enterRangeConstructor(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitRangeConstructor"): + listener.exitRangeConstructor(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitRangeConstructor"): + return visitor.visitRangeConstructor(self) + else: + return visitor.visitChildren(self) + + def rangeConstructor(self): + + localctx = OpenSCENARIO2Parser.RangeConstructorContext(self, self._ctx, self.state) + self.enterRule(localctx, 260, self.RULE_rangeConstructor) + try: + self.state = 1258 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.T__69]: + self.enterOuterAlt(localctx, 1) + self.state = 1245 + self.match(OpenSCENARIO2Parser.T__69) + self.state = 1246 + self.match(OpenSCENARIO2Parser.OPEN_PAREN) + self.state = 1247 + self.expression() + self.state = 1248 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1249 + self.expression() + self.state = 1250 + self.match(OpenSCENARIO2Parser.CLOSE_PAREN) + pass + elif token in [OpenSCENARIO2Parser.OPEN_BRACK]: + self.enterOuterAlt(localctx, 2) + self.state = 1252 + self.match(OpenSCENARIO2Parser.OPEN_BRACK) + self.state = 1253 + self.expression() + self.state = 1254 + self.match(OpenSCENARIO2Parser.T__87) + self.state = 1255 + self.expression() + self.state = 1256 + self.match(OpenSCENARIO2Parser.CLOSE_BRACK) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class IdentifierReferenceContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def fieldName(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.FieldNameContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.FieldNameContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_identifierReference + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIdentifierReference"): + listener.enterIdentifierReference(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIdentifierReference"): + listener.exitIdentifierReference(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIdentifierReference"): + return visitor.visitIdentifierReference(self) + else: + return visitor.visitChildren(self) + + def identifierReference(self): + + localctx = OpenSCENARIO2Parser.IdentifierReferenceContext(self, self._ctx, self.state) + self.enterRule(localctx, 262, self.RULE_identifierReference) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1265 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 118, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + self.state = 1260 + self.fieldName() + self.state = 1261 + self.match(OpenSCENARIO2Parser.T__1) + self.state = 1267 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 118, self._ctx) + + self.state = 1268 + self.fieldName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ArgumentListSpecificationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def argumentSpecification(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.ArgumentSpecificationContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentSpecificationContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_argumentListSpecification + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterArgumentListSpecification"): + listener.enterArgumentListSpecification(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitArgumentListSpecification"): + listener.exitArgumentListSpecification(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitArgumentListSpecification"): + return visitor.visitArgumentListSpecification(self) + else: + return visitor.visitChildren(self) + + def argumentListSpecification(self): + + localctx = OpenSCENARIO2Parser.ArgumentListSpecificationContext(self, self._ctx, self.state) + self.enterRule(localctx, 264, self.RULE_argumentListSpecification) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1270 + self.argumentSpecification() + self.state = 1275 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 1271 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1272 + self.argumentSpecification() + self.state = 1277 + self._errHandler.sync(self) + _la = self._input.LA(1) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ArgumentSpecificationContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def argumentName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentNameContext, 0) + + def typeDeclarator(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.TypeDeclaratorContext, 0) + + def defaultValue(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.DefaultValueContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_argumentSpecification + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterArgumentSpecification"): + listener.enterArgumentSpecification(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitArgumentSpecification"): + listener.exitArgumentSpecification(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitArgumentSpecification"): + return visitor.visitArgumentSpecification(self) + else: + return visitor.visitChildren(self) + + def argumentSpecification(self): + + localctx = OpenSCENARIO2Parser.ArgumentSpecificationContext(self, self._ctx, self.state) + self.enterRule(localctx, 266, self.RULE_argumentSpecification) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1278 + self.argumentName() + self.state = 1279 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 1280 + self.typeDeclarator() + self.state = 1283 + self._errHandler.sync(self) + _la = self._input.LA(1) + if _la == OpenSCENARIO2Parser.T__20: + self.state = 1281 + self.match(OpenSCENARIO2Parser.T__20) + self.state = 1282 + self.defaultValue() + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ArgumentNameContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def Identifier(self): + return self.getToken(OpenSCENARIO2Parser.Identifier, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_argumentName + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterArgumentName"): + listener.enterArgumentName(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitArgumentName"): + listener.exitArgumentName(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitArgumentName"): + return visitor.visitArgumentName(self) + else: + return visitor.visitChildren(self) + + def argumentName(self): + + localctx = OpenSCENARIO2Parser.ArgumentNameContext(self, self._ctx, self.state) + self.enterRule(localctx, 268, self.RULE_argumentName) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1285 + self.match(OpenSCENARIO2Parser.Identifier) + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class ArgumentListContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def positionalArgument(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.PositionalArgumentContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.PositionalArgumentContext, i) + + def namedArgument(self, i: int = None): + if i is None: + return self.getTypedRuleContexts(OpenSCENARIO2Parser.NamedArgumentContext) + else: + return self.getTypedRuleContext(OpenSCENARIO2Parser.NamedArgumentContext, i) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_argumentList + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterArgumentList"): + listener.enterArgumentList(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitArgumentList"): + listener.exitArgumentList(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitArgumentList"): + return visitor.visitArgumentList(self) + else: + return visitor.visitChildren(self) + + def argumentList(self): + + localctx = OpenSCENARIO2Parser.ArgumentListContext(self, self._ctx, self.state) + self.enterRule(localctx, 270, self.RULE_argumentList) + self._la = 0 # Token type + try: + self.state = 1310 + self._errHandler.sync(self) + la_ = self._interp.adaptivePredict(self._input, 124, self._ctx) + if la_ == 1: + self.enterOuterAlt(localctx, 1) + self.state = 1287 + self.positionalArgument() + self.state = 1292 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 121, self._ctx) + while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER: + if _alt == 1: + self.state = 1288 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1289 + self.positionalArgument() + self.state = 1294 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input, 121, self._ctx) + + self.state = 1299 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 1295 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1296 + self.namedArgument() + self.state = 1301 + self._errHandler.sync(self) + _la = self._input.LA(1) + + pass + + elif la_ == 2: + self.enterOuterAlt(localctx, 2) + self.state = 1302 + self.namedArgument() + self.state = 1307 + self._errHandler.sync(self) + _la = self._input.LA(1) + while _la == OpenSCENARIO2Parser.T__7: + self.state = 1303 + self.match(OpenSCENARIO2Parser.T__7) + self.state = 1304 + self.namedArgument() + self.state = 1309 + self._errHandler.sync(self) + _la = self._input.LA(1) + + pass + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class PositionalArgumentContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_positionalArgument + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPositionalArgument"): + listener.enterPositionalArgument(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPositionalArgument"): + listener.exitPositionalArgument(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPositionalArgument"): + return visitor.visitPositionalArgument(self) + else: + return visitor.visitChildren(self) + + def positionalArgument(self): + + localctx = OpenSCENARIO2Parser.PositionalArgumentContext(self, self._ctx, self.state) + self.enterRule(localctx, 272, self.RULE_positionalArgument) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1312 + self.expression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class NamedArgumentContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def argumentName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ArgumentNameContext, 0) + + def expression(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.ExpressionContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_namedArgument + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterNamedArgument"): + listener.enterNamedArgument(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitNamedArgument"): + listener.exitNamedArgument(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitNamedArgument"): + return visitor.visitNamedArgument(self) + else: + return visitor.visitChildren(self) + + def namedArgument(self): + + localctx = OpenSCENARIO2Parser.NamedArgumentContext(self, self._ctx, self.state) + self.enterRule(localctx, 274, self.RULE_namedArgument) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1314 + self.argumentName() + self.state = 1315 + self.match(OpenSCENARIO2Parser.T__8) + self.state = 1316 + self.expression() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class PhysicalLiteralContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def unitName(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.UnitNameContext, 0) + + def FloatLiteral(self): + return self.getToken(OpenSCENARIO2Parser.FloatLiteral, 0) + + def integerLiteral(self): + return self.getTypedRuleContext(OpenSCENARIO2Parser.IntegerLiteralContext, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_physicalLiteral + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterPhysicalLiteral"): + listener.enterPhysicalLiteral(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitPhysicalLiteral"): + listener.exitPhysicalLiteral(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitPhysicalLiteral"): + return visitor.visitPhysicalLiteral(self) + else: + return visitor.visitChildren(self) + + def physicalLiteral(self): + + localctx = OpenSCENARIO2Parser.PhysicalLiteralContext(self, self._ctx, self.state) + self.enterRule(localctx, 276, self.RULE_physicalLiteral) + try: + self.enterOuterAlt(localctx, 1) + self.state = 1320 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [OpenSCENARIO2Parser.FloatLiteral]: + self.state = 1318 + self.match(OpenSCENARIO2Parser.FloatLiteral) + pass + elif token in [OpenSCENARIO2Parser.UintLiteral, OpenSCENARIO2Parser.HexUintLiteral, OpenSCENARIO2Parser.IntLiteral]: + self.state = 1319 + self.integerLiteral() + pass + else: + raise NoViableAltException(self) + + self.state = 1322 + self.unitName() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class IntegerLiteralContext(ParserRuleContext): + + def __init__(self, parser, parent: ParserRuleContext = None, invokingState: int = -1): + super().__init__(parent, invokingState) + self.parser = parser + + def UintLiteral(self): + return self.getToken(OpenSCENARIO2Parser.UintLiteral, 0) + + def HexUintLiteral(self): + return self.getToken(OpenSCENARIO2Parser.HexUintLiteral, 0) + + def IntLiteral(self): + return self.getToken(OpenSCENARIO2Parser.IntLiteral, 0) + + def getRuleIndex(self): + return OpenSCENARIO2Parser.RULE_integerLiteral + + def enterRule(self, listener: ParseTreeListener): + if hasattr(listener, "enterIntegerLiteral"): + listener.enterIntegerLiteral(self) + + def exitRule(self, listener: ParseTreeListener): + if hasattr(listener, "exitIntegerLiteral"): + listener.exitIntegerLiteral(self) + + def accept(self, visitor: ParseTreeVisitor): + if hasattr(visitor, "visitIntegerLiteral"): + return visitor.visitIntegerLiteral(self) + else: + return visitor.visitChildren(self) + + def integerLiteral(self): + + localctx = OpenSCENARIO2Parser.IntegerLiteralContext(self, self._ctx, self.state) + self.enterRule(localctx, 278, self.RULE_integerLiteral) + self._la = 0 # Token type + try: + self.enterOuterAlt(localctx, 1) + self.state = 1324 + _la = self._input.LA(1) + if not (((((_la - 99)) & ~0x3f) == 0 and ((1 << (_la - 99)) & ((1 << (OpenSCENARIO2Parser.UintLiteral - 99)) | (1 << (OpenSCENARIO2Parser.HexUintLiteral - 99)) | (1 << (OpenSCENARIO2Parser.IntLiteral - 99)))) != 0)): + self._errHandler.recoverInline(self) + else: + self._errHandler.reportMatch(self) + self.consume() + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + def sempred(self, localctx: RuleContext, ruleIndex: int, predIndex: int): + if self._predicates == None: + self._predicates = dict() + self._predicates[4] = self.structuredIdentifier_sempred + self._predicates[118] = self.relation_sempred + self._predicates[120] = self.sumExpression_sempred + self._predicates[122] = self.term_sempred + self._predicates[125] = self.postfixExp_sempred + pred = self._predicates.get(ruleIndex, None) + if pred is None: + raise Exception("No predicate with index:" + str(ruleIndex)) + else: + return pred(localctx, predIndex) + + def structuredIdentifier_sempred(self, localctx: StructuredIdentifierContext, predIndex: int): + if predIndex == 0: + return self.precpred(self._ctx, 1) + + def relation_sempred(self, localctx: RelationContext, predIndex: int): + if predIndex == 1: + return self.precpred(self._ctx, 1) + + def sumExpression_sempred(self, localctx: SumExpressionContext, predIndex: int): + if predIndex == 2: + return self.precpred(self._ctx, 1) + + def term_sempred(self, localctx: TermContext, predIndex: int): + if predIndex == 3: + return self.precpred(self._ctx, 1) + + def postfixExp_sempred(self, localctx: PostfixExpContext, predIndex: int): + if predIndex == 4: + return self.precpred(self._ctx, 5) + + if predIndex == 5: + return self.precpred(self._ctx, 4) + + if predIndex == 6: + return self.precpred(self._ctx, 3) + + if predIndex == 7: + return self.precpred(self._ctx, 2) + + if predIndex == 8: + return self.precpred(self._ctx, 1) diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Visitor.py b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Visitor.py new file mode 100644 index 00000000..e306c079 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/OpenSCENARIO2Visitor.py @@ -0,0 +1,642 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# Generated from OpenSCENARIO2.g4 by ANTLR 4.7.2 +from antlr4 import * +if __name__ is not None and "." in __name__: + from .OpenSCENARIO2Parser import OpenSCENARIO2Parser +else: + from OpenSCENARIO2Parser import OpenSCENARIO2Parser + +# This class defines a complete generic visitor for a parse tree produced by OpenSCENARIO2Parser. + + +class OpenSCENARIO2Visitor(ParseTreeVisitor): + + # Visit a parse tree produced by OpenSCENARIO2Parser#osc_file. + def visitOsc_file(self, ctx: OpenSCENARIO2Parser.Osc_fileContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#preludeStatement. + def visitPreludeStatement(self, ctx: OpenSCENARIO2Parser.PreludeStatementContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#importStatement. + def visitImportStatement(self, ctx: OpenSCENARIO2Parser.ImportStatementContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#importReference. + def visitImportReference(self, ctx: OpenSCENARIO2Parser.ImportReferenceContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#structuredIdentifier. + def visitStructuredIdentifier(self, ctx: OpenSCENARIO2Parser.StructuredIdentifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#oscDeclaration. + def visitOscDeclaration(self, ctx: OpenSCENARIO2Parser.OscDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#physicalTypeDeclaration. + def visitPhysicalTypeDeclaration(self, ctx: OpenSCENARIO2Parser.PhysicalTypeDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#physicalTypeName. + def visitPhysicalTypeName(self, ctx: OpenSCENARIO2Parser.PhysicalTypeNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#baseUnitSpecifier. + def visitBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.BaseUnitSpecifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#sIBaseUnitSpecifier. + def visitSIBaseUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SIBaseUnitSpecifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#unitDeclaration. + def visitUnitDeclaration(self, ctx: OpenSCENARIO2Parser.UnitDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#unitSpecifier. + def visitUnitSpecifier(self, ctx: OpenSCENARIO2Parser.UnitSpecifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#unitName. + def visitUnitName(self, ctx: OpenSCENARIO2Parser.UnitNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#siBaseExponentList. + def visitSiBaseExponentList(self, ctx: OpenSCENARIO2Parser.SiBaseExponentListContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#siBaseExponent. + def visitSiBaseExponent(self, ctx: OpenSCENARIO2Parser.SiBaseExponentContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#siUnitSpecifier. + def visitSiUnitSpecifier(self, ctx: OpenSCENARIO2Parser.SiUnitSpecifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#siFactor. + def visitSiFactor(self, ctx: OpenSCENARIO2Parser.SiFactorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#siOffset. + def visitSiOffset(self, ctx: OpenSCENARIO2Parser.SiOffsetContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#siBaseUnitName. + def visitSiBaseUnitName(self, ctx: OpenSCENARIO2Parser.SiBaseUnitNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#enumDeclaration. + def visitEnumDeclaration(self, ctx: OpenSCENARIO2Parser.EnumDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#enumMemberDecl. + def visitEnumMemberDecl(self, ctx: OpenSCENARIO2Parser.EnumMemberDeclContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#enumMemberValue. + def visitEnumMemberValue(self, ctx: OpenSCENARIO2Parser.EnumMemberValueContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#enumName. + def visitEnumName(self, ctx: OpenSCENARIO2Parser.EnumNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#enumMemberName. + def visitEnumMemberName(self, ctx: OpenSCENARIO2Parser.EnumMemberNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#enumValueReference. + def visitEnumValueReference(self, ctx: OpenSCENARIO2Parser.EnumValueReferenceContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#inheritsCondition. + def visitInheritsCondition(self, ctx: OpenSCENARIO2Parser.InheritsConditionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#structDeclaration. + def visitStructDeclaration(self, ctx: OpenSCENARIO2Parser.StructDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#structInherits. + def visitStructInherits(self, ctx: OpenSCENARIO2Parser.StructInheritsContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#structMemberDecl. + def visitStructMemberDecl(self, ctx: OpenSCENARIO2Parser.StructMemberDeclContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#fieldName. + def visitFieldName(self, ctx: OpenSCENARIO2Parser.FieldNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#structName. + def visitStructName(self, ctx: OpenSCENARIO2Parser.StructNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#actorDeclaration. + def visitActorDeclaration(self, ctx: OpenSCENARIO2Parser.ActorDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#actorInherits. + def visitActorInherits(self, ctx: OpenSCENARIO2Parser.ActorInheritsContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#actorMemberDecl. + def visitActorMemberDecl(self, ctx: OpenSCENARIO2Parser.ActorMemberDeclContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#actorName. + def visitActorName(self, ctx: OpenSCENARIO2Parser.ActorNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#scenarioDeclaration. + def visitScenarioDeclaration(self, ctx: OpenSCENARIO2Parser.ScenarioDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#scenarioInherits. + def visitScenarioInherits(self, ctx: OpenSCENARIO2Parser.ScenarioInheritsContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#scenarioMemberDecl. + def visitScenarioMemberDecl(self, ctx: OpenSCENARIO2Parser.ScenarioMemberDeclContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#qualifiedBehaviorName. + def visitQualifiedBehaviorName(self, ctx: OpenSCENARIO2Parser.QualifiedBehaviorNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#behaviorName. + def visitBehaviorName(self, ctx: OpenSCENARIO2Parser.BehaviorNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#actionDeclaration. + def visitActionDeclaration(self, ctx: OpenSCENARIO2Parser.ActionDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#actionInherits. + def visitActionInherits(self, ctx: OpenSCENARIO2Parser.ActionInheritsContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#modifierDeclaration. + def visitModifierDeclaration(self, ctx: OpenSCENARIO2Parser.ModifierDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#modifierName. + def visitModifierName(self, ctx: OpenSCENARIO2Parser.ModifierNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#typeExtension. + def visitTypeExtension(self, ctx: OpenSCENARIO2Parser.TypeExtensionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#enumTypeExtension. + def visitEnumTypeExtension(self, ctx: OpenSCENARIO2Parser.EnumTypeExtensionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#structuredTypeExtension. + def visitStructuredTypeExtension(self, ctx: OpenSCENARIO2Parser.StructuredTypeExtensionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#extendableTypeName. + def visitExtendableTypeName(self, ctx: OpenSCENARIO2Parser.ExtendableTypeNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#extensionMemberDecl. + def visitExtensionMemberDecl(self, ctx: OpenSCENARIO2Parser.ExtensionMemberDeclContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#globalParameterDeclaration. + def visitGlobalParameterDeclaration(self, ctx: OpenSCENARIO2Parser.GlobalParameterDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#typeDeclarator. + def visitTypeDeclarator(self, ctx: OpenSCENARIO2Parser.TypeDeclaratorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#nonAggregateTypeDeclarator. + def visitNonAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.NonAggregateTypeDeclaratorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#aggregateTypeDeclarator. + def visitAggregateTypeDeclarator(self, ctx: OpenSCENARIO2Parser.AggregateTypeDeclaratorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#listTypeDeclarator. + def visitListTypeDeclarator(self, ctx: OpenSCENARIO2Parser.ListTypeDeclaratorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#primitiveType. + def visitPrimitiveType(self, ctx: OpenSCENARIO2Parser.PrimitiveTypeContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#typeName. + def visitTypeName(self, ctx: OpenSCENARIO2Parser.TypeNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#eventDeclaration. + def visitEventDeclaration(self, ctx: OpenSCENARIO2Parser.EventDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#eventSpecification. + def visitEventSpecification(self, ctx: OpenSCENARIO2Parser.EventSpecificationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#eventReference. + def visitEventReference(self, ctx: OpenSCENARIO2Parser.EventReferenceContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#eventFieldDecl. + def visitEventFieldDecl(self, ctx: OpenSCENARIO2Parser.EventFieldDeclContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#eventFieldName. + def visitEventFieldName(self, ctx: OpenSCENARIO2Parser.EventFieldNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#eventName. + def visitEventName(self, ctx: OpenSCENARIO2Parser.EventNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#eventPath. + def visitEventPath(self, ctx: OpenSCENARIO2Parser.EventPathContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#eventCondition. + def visitEventCondition(self, ctx: OpenSCENARIO2Parser.EventConditionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#riseExpression. + def visitRiseExpression(self, ctx: OpenSCENARIO2Parser.RiseExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#fallExpression. + def visitFallExpression(self, ctx: OpenSCENARIO2Parser.FallExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#elapsedExpression. + def visitElapsedExpression(self, ctx: OpenSCENARIO2Parser.ElapsedExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#everyExpression. + def visitEveryExpression(self, ctx: OpenSCENARIO2Parser.EveryExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#boolExpression. + def visitBoolExpression(self, ctx: OpenSCENARIO2Parser.BoolExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#durationExpression. + def visitDurationExpression(self, ctx: OpenSCENARIO2Parser.DurationExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#fieldDeclaration. + def visitFieldDeclaration(self, ctx: OpenSCENARIO2Parser.FieldDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#parameterDeclaration. + def visitParameterDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#variableDeclaration. + def visitVariableDeclaration(self, ctx: OpenSCENARIO2Parser.VariableDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#sampleExpression. + def visitSampleExpression(self, ctx: OpenSCENARIO2Parser.SampleExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#defaultValue. + def visitDefaultValue(self, ctx: OpenSCENARIO2Parser.DefaultValueContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#parameterWithDeclaration. + def visitParameterWithDeclaration(self, ctx: OpenSCENARIO2Parser.ParameterWithDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#parameterWithMember. + def visitParameterWithMember(self, ctx: OpenSCENARIO2Parser.ParameterWithMemberContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#constraintDeclaration. + def visitConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.ConstraintDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#keepConstraintDeclaration. + def visitKeepConstraintDeclaration(self, ctx: OpenSCENARIO2Parser.KeepConstraintDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#constraintQualifier. + def visitConstraintQualifier(self, ctx: OpenSCENARIO2Parser.ConstraintQualifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#constraintExpression. + def visitConstraintExpression(self, ctx: OpenSCENARIO2Parser.ConstraintExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#removeDefaultDeclaration. + def visitRemoveDefaultDeclaration(self, ctx: OpenSCENARIO2Parser.RemoveDefaultDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#parameterReference. + def visitParameterReference(self, ctx: OpenSCENARIO2Parser.ParameterReferenceContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#modifierInvocation. + def visitModifierInvocation(self, ctx: OpenSCENARIO2Parser.ModifierInvocationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#behaviorExpression. + def visitBehaviorExpression(self, ctx: OpenSCENARIO2Parser.BehaviorExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#behaviorSpecification. + def visitBehaviorSpecification(self, ctx: OpenSCENARIO2Parser.BehaviorSpecificationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#onDirective. + def visitOnDirective(self, ctx: OpenSCENARIO2Parser.OnDirectiveContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#onMember. + def visitOnMember(self, ctx: OpenSCENARIO2Parser.OnMemberContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#doDirective. + def visitDoDirective(self, ctx: OpenSCENARIO2Parser.DoDirectiveContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#doMember. + def visitDoMember(self, ctx: OpenSCENARIO2Parser.DoMemberContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#composition. + def visitComposition(self, ctx: OpenSCENARIO2Parser.CompositionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#compositionOperator. + def visitCompositionOperator(self, ctx: OpenSCENARIO2Parser.CompositionOperatorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#behaviorInvocation. + def visitBehaviorInvocation(self, ctx: OpenSCENARIO2Parser.BehaviorInvocationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#behaviorWithDeclaration. + def visitBehaviorWithDeclaration(self, ctx: OpenSCENARIO2Parser.BehaviorWithDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#behaviorWithMember. + def visitBehaviorWithMember(self, ctx: OpenSCENARIO2Parser.BehaviorWithMemberContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#labelName. + def visitLabelName(self, ctx: OpenSCENARIO2Parser.LabelNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#actorExpression. + def visitActorExpression(self, ctx: OpenSCENARIO2Parser.ActorExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#waitDirective. + def visitWaitDirective(self, ctx: OpenSCENARIO2Parser.WaitDirectiveContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#emitDirective. + def visitEmitDirective(self, ctx: OpenSCENARIO2Parser.EmitDirectiveContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#callDirective. + def visitCallDirective(self, ctx: OpenSCENARIO2Parser.CallDirectiveContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#untilDirective. + def visitUntilDirective(self, ctx: OpenSCENARIO2Parser.UntilDirectiveContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#methodInvocation. + def visitMethodInvocation(self, ctx: OpenSCENARIO2Parser.MethodInvocationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#methodDeclaration. + def visitMethodDeclaration(self, ctx: OpenSCENARIO2Parser.MethodDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#returnType. + def visitReturnType(self, ctx: OpenSCENARIO2Parser.ReturnTypeContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#methodImplementation. + def visitMethodImplementation(self, ctx: OpenSCENARIO2Parser.MethodImplementationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#methodQualifier. + def visitMethodQualifier(self, ctx: OpenSCENARIO2Parser.MethodQualifierContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#methodName. + def visitMethodName(self, ctx: OpenSCENARIO2Parser.MethodNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#coverageDeclaration. + def visitCoverageDeclaration(self, ctx: OpenSCENARIO2Parser.CoverageDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#coverDeclaration. + def visitCoverDeclaration(self, ctx: OpenSCENARIO2Parser.CoverDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#recordDeclaration. + def visitRecordDeclaration(self, ctx: OpenSCENARIO2Parser.RecordDeclarationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#coverageExpression. + def visitCoverageExpression(self, ctx: OpenSCENARIO2Parser.CoverageExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#coverageUnit. + def visitCoverageUnit(self, ctx: OpenSCENARIO2Parser.CoverageUnitContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#coverageRange. + def visitCoverageRange(self, ctx: OpenSCENARIO2Parser.CoverageRangeContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#coverageEvery. + def visitCoverageEvery(self, ctx: OpenSCENARIO2Parser.CoverageEveryContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#coverageEvent. + def visitCoverageEvent(self, ctx: OpenSCENARIO2Parser.CoverageEventContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#coverageNameArgument. + def visitCoverageNameArgument(self, ctx: OpenSCENARIO2Parser.CoverageNameArgumentContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#targetName. + def visitTargetName(self, ctx: OpenSCENARIO2Parser.TargetNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#expression. + def visitExpression(self, ctx: OpenSCENARIO2Parser.ExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#ternaryOpExp. + def visitTernaryOpExp(self, ctx: OpenSCENARIO2Parser.TernaryOpExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#implication. + def visitImplication(self, ctx: OpenSCENARIO2Parser.ImplicationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#disjunction. + def visitDisjunction(self, ctx: OpenSCENARIO2Parser.DisjunctionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#conjunction. + def visitConjunction(self, ctx: OpenSCENARIO2Parser.ConjunctionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#inversion. + def visitInversion(self, ctx: OpenSCENARIO2Parser.InversionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#relationExp. + def visitRelationExp(self, ctx: OpenSCENARIO2Parser.RelationExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#sumExp. + def visitSumExp(self, ctx: OpenSCENARIO2Parser.SumExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#relationalOp. + def visitRelationalOp(self, ctx: OpenSCENARIO2Parser.RelationalOpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#termExp. + def visitTermExp(self, ctx: OpenSCENARIO2Parser.TermExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#additiveExp. + def visitAdditiveExp(self, ctx: OpenSCENARIO2Parser.AdditiveExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#additiveOp. + def visitAdditiveOp(self, ctx: OpenSCENARIO2Parser.AdditiveOpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#multiplicativeExp. + def visitMultiplicativeExp(self, ctx: OpenSCENARIO2Parser.MultiplicativeExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#factorExp. + def visitFactorExp(self, ctx: OpenSCENARIO2Parser.FactorExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#multiplicativeOp. + def visitMultiplicativeOp(self, ctx: OpenSCENARIO2Parser.MultiplicativeOpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#factor. + def visitFactor(self, ctx: OpenSCENARIO2Parser.FactorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#primaryExpression. + def visitPrimaryExpression(self, ctx: OpenSCENARIO2Parser.PrimaryExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#castExpression. + def visitCastExpression(self, ctx: OpenSCENARIO2Parser.CastExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#functionApplicationExpression. + def visitFunctionApplicationExpression(self, ctx: OpenSCENARIO2Parser.FunctionApplicationExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#fieldAccessExpression. + def visitFieldAccessExpression(self, ctx: OpenSCENARIO2Parser.FieldAccessExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#elementAccessExpression. + def visitElementAccessExpression(self, ctx: OpenSCENARIO2Parser.ElementAccessExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#typeTestExpression. + def visitTypeTestExpression(self, ctx: OpenSCENARIO2Parser.TypeTestExpressionContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#fieldAccess. + def visitFieldAccess(self, ctx: OpenSCENARIO2Parser.FieldAccessContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#primaryExp. + def visitPrimaryExp(self, ctx: OpenSCENARIO2Parser.PrimaryExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#valueExp. + def visitValueExp(self, ctx: OpenSCENARIO2Parser.ValueExpContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#listConstructor. + def visitListConstructor(self, ctx: OpenSCENARIO2Parser.ListConstructorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#rangeConstructor. + def visitRangeConstructor(self, ctx: OpenSCENARIO2Parser.RangeConstructorContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#identifierReference. + def visitIdentifierReference(self, ctx: OpenSCENARIO2Parser.IdentifierReferenceContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#argumentListSpecification. + def visitArgumentListSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentListSpecificationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#argumentSpecification. + def visitArgumentSpecification(self, ctx: OpenSCENARIO2Parser.ArgumentSpecificationContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#argumentName. + def visitArgumentName(self, ctx: OpenSCENARIO2Parser.ArgumentNameContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#argumentList. + def visitArgumentList(self, ctx: OpenSCENARIO2Parser.ArgumentListContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#positionalArgument. + def visitPositionalArgument(self, ctx: OpenSCENARIO2Parser.PositionalArgumentContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#namedArgument. + def visitNamedArgument(self, ctx: OpenSCENARIO2Parser.NamedArgumentContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#physicalLiteral. + def visitPhysicalLiteral(self, ctx: OpenSCENARIO2Parser.PhysicalLiteralContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by OpenSCENARIO2Parser#integerLiteral. + def visitIntegerLiteral(self, ctx: OpenSCENARIO2Parser.IntegerLiteralContext): + return self.visitChildren(ctx) + + +del OpenSCENARIO2Parser diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/README.md b/scenario_execution_base/scenario_execution_base/osc2_parsing/README.md new file mode 100644 index 00000000..17bbca9e --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/README.md @@ -0,0 +1,3 @@ +# generate antlr parser + +antlr4 -Dlanguage=Python3 -visitor -listener OpenSCENARIO2.g4 \ No newline at end of file diff --git a/scenario_execution_base/scenario_execution_base/osc2_parsing/__init__.py b/scenario_execution_base/scenario_execution_base/osc2_parsing/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/osc2_parsing/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_execution_base/scenario_execution_base/scenario_execution.py b/scenario_execution_base/scenario_execution_base/scenario_execution.py new file mode 100644 index 00000000..3ac9abe0 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/scenario_execution.py @@ -0,0 +1,315 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import sys +import time +import argparse +from datetime import datetime, timedelta +import py_trees +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from scenario_execution_base.model.model_file_loader import ModelFileLoader + + +class LastSnapshotVisitor(py_trees.visitors.DisplaySnapshotVisitor): + + def __init__(self): + self.last_snapshot = "" + super().__init__() + + def finalise(self) -> None: + if self.root is not None: + self.last_snapshot = py_trees.display.unicode_tree( + root=self.root, + show_only_visited=self.display_only_visited_behaviours, + show_status=False, + visited=self.visited, + previously_visited=self.previously_visited + ) + + +class ScenarioExecution(object): + """ + Base class for scenario execution. + Override method run() and method setup_behaviour_tree() to adapt to other middlewares. + This class can also be executed standalone + """ + + def __init__(self, + debug: bool, + log_model: bool, + live_tree: bool, + scenario: str, + test_output: str, + setup_timeout=py_trees.common.Duration.INFINITE, + tick_tock_period: float = 0.1) -> None: + self.debug = debug + self.log_model = log_model + self.live_tree = live_tree + self.scenario = scenario + self.test_output = test_output + self.logger = self._get_logger() + + if self.debug: + py_trees.logging.level = py_trees.logging.Level.DEBUG + self.setup_timeout = setup_timeout + self.tick_tock_period = tick_tock_period + self.scenarios = None + self.blackboard = None + self.behaviour_tree = None + self.last_snapshot_visitor = None + self.shutdown_requested = False + self.results = [] + + def _get_logger(self): + """ + Create a logger + This method could be overriden by child classes and defined according to the middleware. + + return: + A logger which has three logging levels: info, warning, error + """ + return Logger('scenario_execution') + + def setup(self, tree: py_trees.behaviour.Behaviour, **kwargs) -> bool: + """ + Setup each scenario before ticking + + Args: + tree [py_trees.behavior.Behavior]: root of the tree + + return: + True if the scenario is setup without errors + """ + self.shutdown_requested = False + self.blackboard = tree.attach_blackboard_client( + name="MainBlackboardClient", + namespace=tree.name + ) + + self.blackboard.register_key("end", access=py_trees.common.Access.READ) + self.blackboard.register_key("fail", access=py_trees.common.Access.READ) + + # Initialize end and fail events + self.blackboard.register_key("end", access=py_trees.common.Access.WRITE) + self.blackboard.register_key("fail", access=py_trees.common.Access.WRITE) + self.blackboard.end = False + self.blackboard.fail = False + self.behaviour_tree = self.setup_behaviour_tree(tree) # Get the behaviour_tree + self.behaviour_tree.add_pre_tick_handler(self.pre_tick_handler) + self.behaviour_tree.add_post_tick_handler(self.post_tick_handler) + self.last_snapshot_visitor = LastSnapshotVisitor() + self.behaviour_tree.add_visitor(self.last_snapshot_visitor) + if self.debug: + self.behaviour_tree.add_visitor(py_trees.visitors.DebugVisitor()) + if self.live_tree: + self.behaviour_tree.add_visitor( + py_trees.visitors.DisplaySnapshotVisitor( + display_blackboard=True + )) + try: + self.behaviour_tree.setup(timeout=self.setup_timeout, logger=self.logger, **kwargs) + return True + except RuntimeError: + self.logger.error('Setup Timeout exceeded. Aborting...') + return False + except Exception as e: # pylint: disable=broad-except + self.logger.error(f"Error while setting up tree: {e}") + return False + + def setup_behaviour_tree(self, tree): + """ + Setup the behaviour tree. + + For other middleware, a subclass of behaviour_tree might be needed for additional support. + Override this to adapt to other middleware. + + Args: + tree [py_trees.behaviour.Behaviour]: root of the behaviour tree + + return: + py_trees.trees.BehaviourTree + """ + return py_trees.trees.BehaviourTree(tree) + + def parse(self): + """ + Parse the OpenScenario2 file + + return: + True if no errors occured during parsing + """ + file_extension = os.path.splitext(self.scenario)[1] + if file_extension == '.osc': + parser = OpenScenario2Parser(self.logger) + elif file_extension == '.sce': + parser = ModelFileLoader(self.logger) + else: + self.logger.error(f"File '{self.scenario}' has unknown extension '{file_extension}'. Allowed [.osc, .sce]") + return False + + start = datetime.now() + self.scenarios = parser.process_file(self.scenario, self.log_model, self.debug) + if self.scenarios is None: + self.add_result((f'Parsing of {self.scenario}', True, "parsing failed", "", datetime.now() - start)) + return self.scenarios is not None + + def run(self) -> bool: + """ + Run all scenarios + + return: + True if all scenarios are executed successfully + """ + if not self.scenarios: + self.logger.info("No scenarios to execute.") + + failure = False + for tree in self.scenarios: + start = datetime.now() + if not tree: + self.logger.error(f'Scenario {tree.name} has no executables.') + continue + if not self.setup(tree): + failure = True + self.logger.error(f'Scenario {tree.name} failed to setup.') + continue + while not self.shutdown_requested: + try: + self.behaviour_tree.tick() + time.sleep(self.tick_tock_period) + if self.live_tree: + self.logger.debug(py_trees.display.unicode_tree( + root=self.behaviour_tree.root, show_status=True)) + except KeyboardInterrupt: + self.behaviour_tree.interrupt() + self.blackboard.fail = True + break + if self.blackboard.fail: + self.logger.error(f'Scenario {tree.name} failed.') + failure = failure or self.blackboard.fail + self.add_result((tree.name, self.blackboard.fail, "execution failed", "", datetime.now()-start)) + self.cleanup_behaviours(tree) + return not failure + + def add_result(self, result): + self.results.append(result) + + def report_results(self): + if self.test_output and self.results: + self.logger.info(f"Writing results to {self.test_output}...") + failures = 0 + overall_time = timedelta(0) + for result in self.results: + if result[1]: + failures += 1 + overall_time += result[4] + try: + with open(self.test_output, 'w') as out: + out.write('\n') + out.write( + f'\n') + for result in self.results: + out.write(f' \n') + if result[1]: + out.write(f' {result[3]}\n') + out.write(f' \n') + out.write("\n") + except Exception as e: # pylint: disable=broad-except + self.logger.error(f"Could not write junitxml {self.test_output}: {e}") + + def pre_tick_handler(self, behaviour_tree): + """ + Things to do before a round of ticking + """ + if self.live_tree: + self.logger.debug( + f"--------- Scenario {behaviour_tree.root.name}: Run {behaviour_tree.count} ---------") + + def post_tick_handler(self, behaviour_tree): + """ + Things to do after a round of ticking + """ + # Shut down if the root is failed + if self.behaviour_tree.root.status == py_trees.common.Status.FAILURE: + self.blackboard.fail = True + if self.behaviour_tree.root.status == py_trees.common.Status.SUCCESS: + self.blackboard.end = True + self.shutdown_requested = self.blackboard.fail or self.blackboard.end + + def cleanup_behaviours(self, tree): + """ + Run cleanup functions in all behaviors + """ + class CleanupVisitor(py_trees.visitors.VisitorBase): + """ + Helper class to call cleanup functions in all behaviors + """ + + def __init__(self): + super(CleanupVisitor, self).__init__(full=False) + + def run(self, behaviour): + """ + call cleanup method + """ + method = getattr(behaviour, 'cleanup', None) + if callable(method): + method() + + cleanup_visitor = CleanupVisitor() + + for node in tree.iterate(): + cleanup_visitor.run(node) + + @staticmethod + def parse_args(args): + parser = argparse.ArgumentParser() + parser.add_argument('-d', '--debug', action='store_true', help='debugging output') + parser.add_argument('-o', '--log-model', action='store_true', + help='Produce tree output of parsed openscenario2 content') + parser.add_argument('-t', '--live-tree', action='store_true', + help='For debugging: Show current state of py tree') + parser.add_argument('-j', '--test-output', type=str, help='Write test output to file.') + parser.add_argument('scenario', type=str, help='scenario file to execute', nargs='?') + args = parser.parse_args(args) + return args + + +def main(): + """ + main function + """ + args = ScenarioExecution.parse_args(sys.argv[1:]) + scenario_execution = ScenarioExecution(debug=args.debug, + log_model=args.log_model, + live_tree=args.live_tree, + scenario=args.scenario, + test_output=args.test_output) + + result = scenario_execution.parse() + if result: + result = scenario_execution.run() + scenario_execution.report_results() + if result: + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/scenario_execution_base/scenario_execution_base/utils/__init__.py b/scenario_execution_base/scenario_execution_base/utils/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/utils/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_execution_base/scenario_execution_base/utils/logging.py b/scenario_execution_base/scenario_execution_base/utils/logging.py new file mode 100644 index 00000000..16a074d6 --- /dev/null +++ b/scenario_execution_base/scenario_execution_base/utils/logging.py @@ -0,0 +1,110 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +class BaseLogger(object): + """ + Base class for logger for scenario execution + For different middleware that does not have logger, inherit from this class + and override the virtual methods + + Args: + name [str]: name of the logger + """ + + def __init__(self, name: str) -> None: + self.name = name + + def info(self, msg: str) -> None: + """ + Virtual method to log info + + Args: + msg [str]: msg to print + """ + raise NotImplementedError + + def debug(self, msg: str) -> None: + """ + virtual method to log debug info + + Args: + msg [str]: msg to print + """ + raise NotImplementedError + + def warning(self, msg: str) -> None: + """ + Virtual method to log warning + + Args: + msg [str]: msg to print + """ + raise NotImplementedError + + def error(self, msg: str) -> None: + """ + Virtual method to log error + + Args: + msg [str]: msg to print + """ + raise NotImplementedError + + +class Logger(BaseLogger): + """ + Class for logger for scenario execution + + Args: + name [str]: name of the logger + """ + + def info(self, msg: str) -> None: + """ + Print a info with logger name and "[INFO]" in the front. + + Args: + msg [str]: msg to print + """ + print(f'[{self.name}] [INFO] {msg}') + + def debug(self, msg: str) -> None: + """ + Print debug info with logger name and "[DEBUG]" in the front. + + Args: + msg [str]: msg to print + """ + print(f'[{self.name}] [DEBUG] {msg}') + + def warning(self, msg: str) -> None: + """ + Print a warning in yellow color with logger name and "[WARN]" in the front. + + Args: + msg [str]: msg to print + """ + print(f'\033[33m[{self.name}] [WARN] {msg}\033[0m') + + def error(self, msg: str) -> None: + """ + Print a warning in red color with logger name and "[ERROR]" in the front. + + Args: + msg [str]: msg to print + """ + print(f'\033[31m[{self.name}] [ERROR] {msg}\033[0m') diff --git a/scenario_execution_base/setup.cfg b/scenario_execution_base/setup.cfg new file mode 100644 index 00000000..77c4ce28 --- /dev/null +++ b/scenario_execution_base/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/scenario_execution_base +[install] +install_scripts=$base/lib/scenario_execution_base diff --git a/scenario_execution_base/setup.py b/scenario_execution_base/setup.py new file mode 100644 index 00000000..a9eb1f6a --- /dev/null +++ b/scenario_execution_base/setup.py @@ -0,0 +1,56 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +"""Setup python package""" +from glob import glob +import os +from setuptools import find_packages, setup + +PACKAGE_NAME = 'scenario_execution_base' + +setup( + name=PACKAGE_NAME, + version='1.0.0', + packages=find_packages(), + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + PACKAGE_NAME]), + ('share/' + PACKAGE_NAME, ['package.xml']), + (os.path.join('share', PACKAGE_NAME, 'launch'), glob('launch/*launch.py')) + ], + install_requires=['setuptools'], + zip_safe=True, + include_package_data=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Robotics Scenario Execution', + license='Apache License 2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'scenario_execution_base = scenario_execution_base.scenario_execution:main', + ], + 'scenario_execution.actions': [ + 'log = scenario_execution_base.actions.log:Log', + 'run_external_process = scenario_execution_base.actions.run_external_process:RunExternalProcess', + ], + 'scenario_execution.osc_libraries': [ + 'helpers = scenario_execution_base.get_osc_library:get_helpers_library', + 'standard = scenario_execution_base.get_osc_library:get_standard_library', + 'robotics = scenario_execution_base.get_osc_library:get_robotics_library', + ] + }, +) diff --git a/scenario_execution_base/test/test_expression.py b/scenario_execution_base/test/test_expression.py new file mode 100644 index 00000000..d5ca8240 --- /dev/null +++ b/scenario_execution_base/test/test_expression.py @@ -0,0 +1,153 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestExpression(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + """ + Unit test for expression parsing + """ + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + @unittest.skip(reason="requires porting") + def test_add(self): + scenario_content = """ +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) +unit ms of time is SI(s: 1, factor: 0.001) + +global test1: float = 2.0 + 1.1 +global test2: time = 2.0s + 1.1s +global test3: time = 2.0s + 1ms +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[3].get_resolved_value(), 3.1) + self.assertEqual(model._ModelElement__children[4].get_resolved_value(), 3.1) + self.assertEqual(model._ModelElement__children[5].get_resolved_value(), 2.001) + + @unittest.skip(reason="requires porting") + def test_substract(self): + scenario_content = """ +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) +unit ms of time is SI(s: 1, factor: 0.001) + +global test1: float = 2.0 - 1.1 +global test2: time = 2.0s - 1.1s +global test3: time = 2.0s - 1ms +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[3].get_resolved_value(), 0.9) + self.assertEqual(model._ModelElement__children[4].get_resolved_value(), 0.9) + self.assertEqual(model._ModelElement__children[5].get_resolved_value(), 1.999) + + @unittest.skip(reason="requires porting") + def test_multiply(self): + scenario_content = """ +type time is SI(s: 1) +unit ms of time is SI(s: 1, factor: 0.001) + +global test1: float = 2.0 * 1.1 +global test2: time = 2.0ms * 1.1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[3].get_resolved_value(), 2.2) + self.assertEqual(model._ModelElement__children[4].get_resolved_value(), 0.0022) + + @unittest.skip(reason="requires porting") + def test_divide(self): + scenario_content = """ +type time is SI(s: 1) +unit ms of time is SI(s: 1, factor: 0.001) + +global test1: float = 5.0 / 2.0 +global test2: time = 5.0ms / 2.0 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[3].get_resolved_value(), 2.5) + self.assertEqual(model._ModelElement__children[4].get_resolved_value(), 0.0025) + + @unittest.skip(reason="requires porting") + def test_relation(self): + scenario_content = """ +type time is SI(s: 1) +unit ms of time is SI(s: 1, factor: 0.001) + +global test1: bool = 5.0 > 2.0 +global test2: bool = 5 > 2 +global test3: bool = 5 < 2 +global test4: bool = 5 == 2 +global test5: bool = 5 != 2 +global test6: bool = 5 >= 2 +global test7: bool = 5 <= 2 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[3].get_resolved_value(), True) + self.assertEqual(model._ModelElement__children[4].get_resolved_value(), True) + self.assertEqual(model._ModelElement__children[5].get_resolved_value(), False) + self.assertEqual(model._ModelElement__children[6].get_resolved_value(), False) + self.assertEqual(model._ModelElement__children[7].get_resolved_value(), True) + self.assertEqual(model._ModelElement__children[8].get_resolved_value(), True) + self.assertEqual(model._ModelElement__children[9].get_resolved_value(), False) + + @unittest.skip(reason="requires porting") + def test_negation(self): + scenario_content = """ +global test1: bool = not True +global test1: bool = not 5 > 2 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[0].get_resolved_value(), False) + self.assertEqual(model._ModelElement__children[1].get_resolved_value(), False) + + @unittest.skip(reason="requires porting") + def test_compound_expression(self): + scenario_content = """ +global test1: bool = 2 > 1 and 3 >= 2 +global test1: bool = 2 > 1 or 3 < 2 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[0].get_resolved_value(), True) + self.assertEqual(model._ModelElement__children[1].get_resolved_value(), True) diff --git a/scenario_execution_base/test/test_model_ser_deser.py b/scenario_execution_base/test/test_model_ser_deser.py new file mode 100644 index 00000000..4e24c4b7 --- /dev/null +++ b/scenario_execution_base/test/test_model_ser_deser.py @@ -0,0 +1,126 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test for Intermediate Scenario model Serialization and Deserialization +""" +import unittest + +from scenario_coverage.scenario_variation import ScenarioVariation +from scenario_execution_base.model.model_file_loader import ModelFileLoader +from scenario_execution_base.utils.logging import BaseLogger, Logger +from scenario_execution_base.model.types import print_tree +import tempfile +import os + + +class DebugLogger(BaseLogger): + + def __init__(self, name): + super().__init__(name) + self.logs = [] + + def info(self, msg: str) -> None: + self.logs.append(msg) + + def debug(self, msg: str) -> None: + self.logs.append(msg) + + def warning(self, msg: str) -> None: + self.logs.append(msg) + + def error(self, msg: str) -> None: + self.logs.append(msg) + + def count_lines(self): + return len(self.logs) + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + def setUp(self) -> None: + self.tmpdir = tempfile.TemporaryDirectory() + + def run_coverage(self, scenario_content): + fp = tempfile.NamedTemporaryFile(suffix='.osc', mode='w', delete=False) + fp.write(scenario_content) + fp.close() + coverage = ScenarioVariation(self.tmpdir.name, fp.name, False, False) + model = coverage.load_model() + del fp + return model, coverage + + def test_serialize(self): + scenario_content = """ +import osc.standard + +action log: + msg: string + +scenario test: + do serial: + log() with: + keep(it.msg in ["foo", "bar"]) + emit end +""" + # serialize + model, coverage = self.run_coverage(scenario_content) + self.assertIsNotNone(model) + models = coverage.generate_concrete_models(model) + self.assertIsNotNone(models) + self.assertEqual(2, len(models)) + result = coverage.save_resulting_scenarios(models) + self.assertTrue(result) + + # deserialize + dir_content = os.listdir(self.tmpdir.name) + scenarios = [] + for entry in dir_content: + if entry.endswith(".sce"): + scenarios.append(os.path.join(self.tmpdir.name, entry)) + self.assertEqual(2, len(scenarios)) + print(scenarios) + scenario0 = None + scenario1 = None + for scenario in scenarios: + if scenario.endswith("0.sce"): + scenario0 = scenario + if scenario.endswith("1.sce"): + scenario1 = scenario + parser = ModelFileLoader(Logger('test')) + models_deserialized = [] + model0 = parser.load_file(scenario0, False) + models_deserialized.append(model0) + model1 = parser.load_file(scenario1, False) + models_deserialized.append(model1) + self.assertIsNotNone(models_deserialized) + self.assertEqual(2, len(models_deserialized)) + + # validate models + deserialize_model_0 = DebugLogger('deserialize_model_0') + base_model_0 = DebugLogger('base_model_0') + print_tree(models_deserialized[0], deserialize_model_0) + print_tree(models[0], base_model_0) + self.assertListEqual(deserialize_model_0.logs, base_model_0.logs) + + deserialize_model_1 = DebugLogger('deserialize_model_1') + base_model_1 = DebugLogger('base_model_1') + print_tree(models_deserialized[1], deserialize_model_1) + print_tree(models[1], base_model_1) + self.assertListEqual(deserialize_model_1.logs, base_model_1.logs) + + # test model lines + self.assertGreater(deserialize_model_0.count_lines(), 0) diff --git a/scenario_execution_base/test/test_osc2_parser_action.py b/scenario_execution_base/test/test_osc2_parser_action.py new file mode 100644 index 00000000..4891b23e --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_action.py @@ -0,0 +1,63 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test action parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_action_valid(self): + scenario_content = """ +action test_action: + param1: string = "value1" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + def test_action_unknown_actor(self): + scenario_content = """ +action UNKNOWN.test_action: + param1: string = "value1" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_action_valid_actor(self): + scenario_content = """ +actor base + +action base.test_action: + param1: string = "value1" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) diff --git a/scenario_execution_base/test/test_osc2_parser_actor.py b/scenario_execution_base/test/test_osc2_parser_actor.py new file mode 100644 index 00000000..fc2da241 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_actor.py @@ -0,0 +1,73 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test actor parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_actor(self): + scenario_content = """ +actor base: + param1: string = "value1" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[0].actor, "base") + self.assertEqual(model._ModelElement__children[0]._ModelElement__children[0].get_resolved_value(), "value1") + + def test_actor_inheritance(self): + scenario_content = """ +actor base: + param1: string = "value1" +actor derived inherits base: + param2: string = "value2" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[0]._ModelElement__children[0].get_resolved_value(), "value1") + self.assertEqual(model._ModelElement__children[1]._ModelElement__children[1].get_resolved_value(), "value2") + + def test_actor_inheritance_override(self): # TODO: expected? + scenario_content = """ +actor base: + param1: string = "value1" +actor derived inherits base: + param1: string = "OVERRIDE" + param2: string = "value2" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + params = model._ModelElement__children[1].get_resolved_value() + self.assertEqual({'param1': 'OVERRIDE', 'param2': 'value2'}, params) diff --git a/scenario_execution_base/test/test_osc2_parser_behavior_invocation.py b/scenario_execution_base/test/test_osc2_parser_behavior_invocation.py new file mode 100644 index 00000000..682aa8fe --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_behavior_invocation.py @@ -0,0 +1,416 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test behavior invocation parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_behavior_invocation_param_override(self): + scenario_content = """ +actor osc_object: + name: string = 'amr' + model_file: string = 'amr' + +struct pose_3d: + x: string = 'x_val' + +action osc_object.spawn: + spawn_pose: pose_3d + world_name: string = 'default' + namespace: string = '' + entity_name: string = '' + model_file: string = '' + xacro_arguments: string = '' # comma-separated list of argument key:=value pairs + +scenario test: + test_obstacle1: osc_object with: + keep(it.name == 'test_obstacle1') + keep(it.model_file == 'test_model') + do parallel: + test_drive: serial: + spawn_it: test_obstacle1.spawn() with: + keep(it.namespace == 'bll') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + behavior = model._ModelElement__children[3]._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + params = behavior.get_resolved_value() + self.assertEqual({'spawn_pose': {'x': 'x_val'}, 'world_name': 'default', + 'namespace': 'bll', 'entity_name': '', 'model_file': '', 'xacro_arguments': ''}, params) + + def test_behavior_named_arg(self): + scenario_content = """ +actor osc_object + +action osc_object.spawn: + param1: string = 'val1' + param2: string = 'val2' + +scenario test: + test_obstacle1: osc_object + do parallel: + spawn_it: test_obstacle1.spawn(param1: 'override1') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + behavior = model._ModelElement__children[2]._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0] + params = behavior.get_resolved_value() + self.assertEqual(params, {'param1': 'override1', 'param2': 'val2'}) + + def test_behavior_from_base_actor(self): + scenario_content = """ +actor osc_object + +action osc_object.spawn: + param1: string + +actor robot inherits osc_object + +scenario test: + test_obstacle1: robot + do parallel: + spawn_it: test_obstacle1.spawn(param1: 'override1') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + def test_behavior_invalid_param_name(self): + scenario_content = """ +actor osc_object + +action osc_object.spawn: + name: string = 'NOTALLOWED' + +scenario test: + test_obstacle1: osc_object + do parallel: + spawn_it: test_obstacle1.spawn(param1: 'override1') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_behavior_invalid_param_associated_actor(self): + scenario_content = """ +actor osc_object + +action osc_object.spawn: + associated_actor: string = 'NOTALLOWED' + param1: string + +scenario test: + test_obstacle1: osc_object + do parallel: + spawn_it: test_obstacle1.spawn(param1: 'override1') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_behavior_positional_arg(self): + scenario_content = """ +action test: + param1: string = 'base' + +scenario test: + do parallel: + spawn_it: test('override1') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + behavior = model._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + params = behavior.get_resolved_value() + self.assertEqual(params, {'param1': 'override1'}) + + @unittest.skip(reason="requires porting") + def test_behavior_empty_args(self): + scenario_content = """ +action test: + param1: string + param2: string + +scenario test: + do parallel: + spawn_it: test() +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + @unittest.skip(reason="requires porting") + def test_behavior_partially_defined_args(self): + scenario_content = """ +action test: + param1: string = 'val1' + param2: string + +scenario test: + do parallel: + spawn_it: test() +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + @unittest.skip(reason="requires porting") + def test_behavior_partially_overridden_positional_args(self): + scenario_content = """ +action test: + param1: string + param2: string + +scenario test: + do parallel: + spawn_it: test('override1') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + @unittest.skip(reason="requires porting") + def test_behavior_partially_overridden_named_args(self): + scenario_content = """ +action test: + param1: string + param2: string + +scenario test: + do parallel: + spawn_it: test(param2: 'override2') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_behavior_too_many_named_args(self): + scenario_content = """ +action test: + param1: string + param2: string + +scenario test: + do parallel: + spawn_it: test(param1: 'override1', param2: 'override2', param3: 'override3') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_behavior_too_many_positional_args(self): + scenario_content = """ +action test: + param1: string + param2: string + +scenario test: + do parallel: + spawn_it: test('override1', 'override2', 'override3') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_behavior_unknown_named_args(self): + scenario_content = """ +action test: + param1: string + param2: string + +scenario test: + do parallel: + spawn_it: test(param1: 'override1', UNKNOWN: 'override2') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + @unittest.skip(reason="requires porting") + def test_behavior_struct_param_named_override(self): + scenario_content = """ +struct test_struct: + param1: string = "val1" + param2: string = "val2" + +action test_action: + struct_param: test_struct + +scenario test: + do serial: + test_action() with: + keep(it.struct_param == test_struct(param2: 'OVERRIDE')) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + behavior = model._ModelElement__children[2]._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[1] + params = behavior.get_resolved_value() + self.assertEqual(params, {'struct_param': {'param1': 'val1', 'param2': 'OVERRIDE'}}) + + def test_behavior_struct_param_positional_override(self): + scenario_content = """ +struct test_struct: + param1: string = "val1" + param2: string = "val2" + +action test_action: + struct_param: test_struct + +scenario test: + do serial: + test_action() with: + keep(it.struct_param == test_struct('OVERRIDE')) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + behavior = model._ModelElement__children[2]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + params = behavior.get_resolved_value() + self.assertEqual(params, {'struct_param': {'param1': 'OVERRIDE', 'param2': 'val2'}}) + + def test_behavior_struct_param_partially_named_positional_override(self): + scenario_content = """ +struct test_struct: + param1: string = "val1" + param2: string = "val2" + param3: string = "val3" + +action test_action: + struct_param: test_struct + +scenario test: + do serial: + test_action() with: + keep(it.struct_param == test_struct('OVERRIDE1', param3: 'OVERRIDE3')) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + behavior = model._ModelElement__children[2]._ModelElement__children[0]._ModelElement__children[0]._ModelElement__children[0] + params = behavior.get_resolved_value() + self.assertEqual(params, {'struct_param': {'param1': 'OVERRIDE1', + 'param2': 'val2', 'param3': 'OVERRIDE3'}}) + + @unittest.skip(reason="requires porting") + def test_behavior_struct_param_spec_incomplete(self): + scenario_content = """ +struct test_struct: + param1: string = "val1" + param2: string + +action test_action: + struct_param: test_struct + +scenario test: + do serial: + test_action() with: + keep(it.struct_param == test_struct('OVERRIDE1')) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_base_vals(self): + scenario_content = """ +type length is SI(m: 1) +unit m of length is SI(m: 1, factor: 1) +type angle is SI(rad: 1) +unit rad of angle is SI(rad: 1, factor: 1) + +actor osc_object: + model: string + +struct position_3d: + x: length = 0m + y: length = 0m + z: length = 0m + +struct orientation_3d: + roll: angle = 0rad + pitch: angle = 0rad + yaw: angle = 0rad + +struct pose_3d: + position: position_3d + orientation: orientation_3d + +action osc_object.spawn: + spawn_pose: pose_3d + +actor differential_drive_robot inherits osc_object: + namespace: string = '' + +scenario nav2_simulation_nav_to_pose: + turtlebot4: differential_drive_robot with: + keep(it.model == 'topic:///robot_description') + do parallel: + turtlebot4.spawn() with: # spawn the robot + keep(it.spawn_pose.position.x == 0.0m) + keep(it.spawn_pose.position.y == 0.0m) + keep(it.spawn_pose.position.z == 0.1m) + keep(it.spawn_pose.orientation.yaw == 0.0rad) + keep(it.spawn_pose.orientation.roll == 1.0rad) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + pose_struct = model._ModelElement__children[7].get_resolved_value() + self.assertEqual({'position': {'x': 0., 'y': 0., 'z': 0.}, 'orientation': { + 'roll': 0., 'pitch': 0., 'yaw': 0.}}, pose_struct) + + behavior = model._ModelElement__children[10]._ModelElement__children[1]._ModelElement__children[0]._ModelElement__children[0].get_resolved_value( + ) + self.assertEqual({'spawn_pose': {'position': {'x': 0., 'y': 0., 'z': 0.1}, + 'orientation': {'roll': 1., 'pitch': 0., 'yaw': 0.}}}, behavior) diff --git a/scenario_execution_base/test/test_osc2_parser_enum.py b/scenario_execution_base/test/test_osc2_parser_enum.py new file mode 100644 index 00000000..8d640294 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_enum.py @@ -0,0 +1,152 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test enum parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + """ + Unit test for osc2_parser + """ + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_enum_ref(self): + scenario_content = """ +enum test_enum: [ + val1, + val2 +] +struct test: + param1: test_enum = test_enum!val1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + test_struct = model._ModelElement__children[1] + self.assertEqual(test_struct.get_resolved_value(), {'param1': ('val1', 0)}) + + def test_enum_ref_invalid(self): + scenario_content = """ +enum test_enum: [ + val1, + val2 +] +struct test: + param1: test_enum = test_enum!UNKNOWN +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_enum_ref_invalid_type(self): + scenario_content = """ +enum test_enum: [ + val1, + val2 +] +struct test: + param1: test_enum = UNKNOWN!val1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_enum_ref_other_enum_type(self): + scenario_content = """ +enum other_enum: [ + val1, + val2 +] + +enum test_enum: [ + val1, + val2 +] + +struct test: + param1: test_enum = other_enum!val1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_enum_val_non_numeric(self): + scenario_content = """ +enum test_enum: [ + val1, + val2 +] + +struct test: + param1: test_enum = test_enum!val1 + param2: test_enum = test_enum!val2 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + test_struct = model._ModelElement__children[1] + self.assertEqual(test_struct.get_resolved_value(), {'param1': ('val1', 0), 'param2': ('val2', 1)}) + + def test_enum_partially_numeric(self): + scenario_content = """ +enum test_enum: [ + val1 = 4, + val2 +] + +struct test: + param1: test_enum = test_enum!val1 + param2: test_enum = test_enum!val2 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + test_struct = model._ModelElement__children[1] + self.assertEqual(test_struct.get_resolved_value(), {'param1': ('val1', 4), 'param2': ('val2', 5)}) + + def test_enum_fully_numeric(self): + scenario_content = """ +enum test_enum: [ + val1 = 4, + val2 = 19 +] + +struct test: + param1: test_enum = test_enum!val1 + param2: test_enum = test_enum!val2 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + test_struct = model._ModelElement__children[1] + self.assertEqual(test_struct.get_resolved_value(), {'param1': ('val1', 4), 'param2': ('val2', 19)}) diff --git a/scenario_execution_base/test/test_osc2_parser_event.py b/scenario_execution_base/test/test_osc2_parser_event.py new file mode 100644 index 00000000..e3acb307 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_event.py @@ -0,0 +1,53 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test event parsing +""" +import unittest +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestScenarioExectionSuccess(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_unknown_event(self): + scenario_content = """ +scenario test: + do serial: + emit UNKNOWN +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_event_success(self): + scenario_content = """ +scenario test: + event test1 + do serial: + emit test1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) diff --git a/scenario_execution_base/test/test_osc2_parser_keep.py b/scenario_execution_base/test/test_osc2_parser_keep.py new file mode 100644 index 00000000..b870c0b6 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_keep.py @@ -0,0 +1,49 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test keep parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_keep_override(self): + scenario_content = """ +actor osc_object + +actor differential_drive_robot inherits osc_object: + namespace: string = '' + +scenario nav2_simulation_nav_to_pose: + turtlebot4: differential_drive_robot with: + keep(it.namespace == 'test') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + robot = model._ModelElement__children[2]._ModelElement__children[0].get_resolved_value() + self.assertEqual({'namespace': 'test'}, robot) diff --git a/scenario_execution_base/test/test_osc2_parser_list.py b/scenario_execution_base/test/test_osc2_parser_list.py new file mode 100644 index 00000000..fcc4c716 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_list.py @@ -0,0 +1,143 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test list parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + """ + Unit test for osc2_parser + """ + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_invalid_listof(self): + scenario_content = """ +struct listoffoo + +global test: listoffoo +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_string_list(self): + scenario_content = """ +global test: list of string = ["foo", "bar"] +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + test_list = model._ModelElement__children[0].get_resolved_value() + self.assertEqual(["foo", "bar"], test_list) + + def test_empty_list(self): + scenario_content = """ +global test: list of string = [ ] +""" + _, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 1) + + def test_mixed_basetypes_list(self): + scenario_content = """ +global test: list of string = ["foo", 3] +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_struct_list(self): + scenario_content = """ +struct test_struct: + member: string +global test: list of test_struct = [test_struct('val1'), test_struct('val2')] +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + test_list = model._ModelElement__children[1].get_resolved_value() + self.assertEqual([{'member': 'val1'}, {'member': 'val2'}], test_list) + + def test_different_structs_in_list(self): + scenario_content = """ +struct test_struct: + member: string +struct second_struct: + member2: string +global test: list of test_struct = [test_struct('val1'), second_struct('invalid')] +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_assign_struct_list(self): + scenario_content = """ +struct test_struct: + member: string + +global test1: list of test_struct = [test_struct('val1'), test_struct('val2')] +global test2: list of test_struct = test1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + test1 = model._ModelElement__children[1].get_resolved_value() + test2 = model._ModelElement__children[2].get_resolved_value() + self.assertEqual([{'member': 'val1'}, {'member': 'val2'}], test1) + self.assertEqual([{'member': 'val1'}, {'member': 'val2'}], test2) + + def test_assign_basetype_list(self): + scenario_content = """ +global test1: list of float = [2.1, 4.3] +global test2: list of float = test1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + test1 = model._ModelElement__children[0].get_resolved_value() + test2 = model._ModelElement__children[1].get_resolved_value() + self.assertEqual([2.1, 4.3], test1) + self.assertEqual([2.1, 4.3], test2) + + def test_assign_wrong_basetype_list(self): + scenario_content = """ +global test1: list of float = [2.1, 4.3] +global test2: list of string = test1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) diff --git a/scenario_execution_base/test/test_osc2_parser_not_supported.py b/scenario_execution_base/test/test_osc2_parser_not_supported.py new file mode 100644 index 00000000..7caef966 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_not_supported.py @@ -0,0 +1,274 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test parsing error reporting for features that are not yet supported +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable, too-many-public-methods + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_method(self): + scenario_content = """ +actor test: + def get_value() -> float is external TEST() +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_cover(self): + scenario_content = """ +scenario test: + cover() +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_lt_expr(self): + scenario_content = """ +actor car: + current_position: float + keep(current_position < 100.0) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_gt_expr(self): + scenario_content = """ +actor car: + current_position: float + keep(current_position > 100.0) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_le_expr(self): + scenario_content = """ +actor car: + current_position: float + keep(current_position <= 100.0) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_ge_expr(self): + scenario_content = """ +actor car: + current_position: float + keep(current_position >= 100.0) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_range(self): + scenario_content = """ +global test: float = range(0,1) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_element_access(self): + scenario_content = """ +global foo: list of float +global test: float = foo[2] +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_type_test(self): + scenario_content = """ +global foo: float +global test: bool = foo.is(float) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_cast(self): + scenario_content = """ +global foo: float +global test: int = foo.as(int) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_sample(self): + scenario_content = """ +import osc.standard + +scenario simple_drive: + environment: environment + var sim_start_time: time = sample(environment.datetime, @root_phase.start) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_every(self): + scenario_content = """ +scenario test: + event test is every(1) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_rise(self): + scenario_content = """ +scenario test: + event test is rise(true) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_fall(self): + scenario_content = """ +scenario test: + event test is fall(true) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_modifier(self): + scenario_content = """ +modifier test +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_modifier_application(self): + scenario_content = """ +modifier test_modifier: + param1: string + +action test_action: + param2: string + +scenario test: + do serial: + test_action() with: + test_modifier(6) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_until(self): + scenario_content = """ +scenario test: + event ev + do serial: + test_action() with: + until @ev +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_call(self): + scenario_content = """ +scenario test: + do serial: + call log(true) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_remove_default(self): + scenario_content = """ +struct base: + param_base: string = "base" + +struct test: + param1: base = base with: + remove_default(it.param_base) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_record(self): + scenario_content = """ +scenario test: + record() +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_enum_extend(self): + scenario_content = """ +enum test: [a, b] +extend test: [c] +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_conditional_inheritance(self): + scenario_content = """ +struct vehicle: + is_electric: bool + +actor electric_vehicle inherits vehicle(is_electric == true) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) diff --git a/scenario_execution_base/test/test_osc2_parser_parameter.py b/scenario_execution_base/test/test_osc2_parser_parameter.py new file mode 100644 index 00000000..d2e2f710 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_parameter.py @@ -0,0 +1,522 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test parameter parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable, too-many-public-methods + """ + Unit test for osc2_parser + """ + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_invalid(self): + scenario_content = """ +invalid +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 1) + + def test_empty(self): + scenario_content = "" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertEqual([None], scenarios) + + def test_global_var(self): + scenario_content = """ +global level1: string = "hey!" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + param = model._ModelElement__children[0].get_resolved_value() + self.assertEqual(param, "hey!") + + def test_global_var_indirect_1(self): + scenario_content = """ +global level1: string = "hey!" +global level2: string = level1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(len(model._ModelElement__children), 2) + glob_param1 = model._ModelElement__children[0] + val1 = glob_param1.get_resolved_value() + self.assertEqual(val1, "hey!") + glob_param2 = model._ModelElement__children[1] + val2 = glob_param2.get_resolved_value() + self.assertEqual(val2, "hey!") + + def test_global_var_indirect_2(self): + scenario_content = """ +global level1: string = "hey!" +global level2: string = level1 +global level3: string = level2 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[0].get_resolved_value(), 'hey!') + self.assertEqual(model._ModelElement__children[1].get_resolved_value(), 'hey!') + self.assertEqual(model._ModelElement__children[2].get_resolved_value(), 'hey!') + + def test_global_var_indirect_2_wrong_type(self): + scenario_content = """ +global level1: string = "hey!" +global level2: int = level1 +global level3: string = level2 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_global_var_indirect_3(self): + scenario_content = """ +global level1: string = "hey!" +global level2: string = level1 +global level3: string = level2 + +scenario test: + var test_var: string = level3 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual(model._ModelElement__children[0].get_resolved_value(), 'hey!') + self.assertEqual(model._ModelElement__children[1].get_resolved_value(), 'hey!') + self.assertEqual(model._ModelElement__children[2].get_resolved_value(), 'hey!') + self.assertEqual(model._ModelElement__children[3]._ModelElement__children[0].get_resolved_value(), 'hey!') + + def test_global_var_indirect_not_found(self): + scenario_content = """ +global level1: string = "hey!" +global level2: int = levelUNKNOWN +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_global_var_indirect_in_struct(self): + scenario_content = """ +global z_param: string = "z_global" +struct test_struct: + z: string = z_param +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual({'z': 'z_global'}, model._ModelElement__children[1].get_resolved_value()) + + def test_global_var_indirect_in_actor(self): + scenario_content = """ +global z_param: string = "z_global" +actor test_struct: + z: string = z_param +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual({'z': 'z_global'}, model._ModelElement__children[1].get_resolved_value()) + + def test_var_instance_unknown_type(self): + scenario_content = """ +scenario test: + test_pose: unknownType +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_var_instance_keep_not_it(self): + scenario_content = """ +struct pose3d: + z: string = "base" +scenario network_drop_straight: + test_pose: pose3d with: + keep(NO.z == "override") +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_var_instance_keep_unknown_member(self): + scenario_content = """ +struct pose3d: + z: string = "base" +scenario network_drop_straight: + test_pose: pose3d with: + keep(it.UNKNOWN == "override") +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_var_instance_keep_success(self): + scenario_content = """ +struct pose3d: + z: string = "base" +scenario network_drop_straight: + test_pose: pose3d with: + keep(it.z == "override") +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + def test_var_instance_keep_not_equal(self): + scenario_content = """ +struct pose3d: + z: string = "base" +scenario network_drop_straight: + test_pose: pose3d with: + keep(it.z <= "override") +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_struct_inheritance(self): + scenario_content = """ +struct base: + x: string = "base" + +struct derived inherits base: + y: string = "derived" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual({'x': 'base', 'y': 'derived'}, + model._ModelElement__children[1].get_resolved_value()) + + def test_actor_inheritance(self): + scenario_content = """ +actor base: + x: string = "base" + +actor derived inherits base: + y: string = "derived" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual({'x': 'base', 'y': 'derived'}, + model._ModelElement__children[1].get_resolved_value()) + + def test_actor_inheritance_invalid(self): + scenario_content = """ +actor derived inherits UNKNOWN: + y: string = "derived" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + scenario_content = """ +action base: + x: string = "base" + +action derived inherits base: + y: string = "derived" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + self.assertEqual({'x': 'base', 'y': 'derived'}, + model._ModelElement__children[1].get_resolved_value()) + + def test_action_inheritance_invalid(self): + scenario_content = """ +action derived inherits UNKNOWN: + y: string = "derived" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_scenario_inheritance(self): + scenario_content = """ +scenario base: + x: string = "base" + +scenario derived inherits base: + y: string = "derived" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + derived = model._ModelElement__children[1] + self.assertEqual({'x': 'base', 'y': 'derived'}, derived.get_resolved_value()) + + def test_scenario_inheritance_invalid(self): + scenario_content = """ +scenario derived inherits UNKNOWN: + y: string = "derived" +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_parameter_to_dict(self): + scenario_content = """ +struct test_struct: + x: string = "value_x" + y: string = "value_y" + +scenario test: + foo: test_struct with: + keep(it.x == "override_x") +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + param = model._ModelElement__children[1]._ModelElement__children[0] + values = param.get_resolved_value() + self.assertEqual({"x": "override_x", "y": "value_y"}, values) + + def test_struct_with_keep(self): + scenario_content = """ +struct test_struct: + x: string = "value_x" + y: string = "value_y" + +global foo: test_struct with: + keep(it.x == "override_x") +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + param = model._ModelElement__children[1] + values = param.get_resolved_value() + self.assertEqual({"x": "override_x", "y": "value_y"}, values) + + @unittest.skip(reason="requires porting") + def test_struct_with_keep_member_unknown(self): + scenario_content = """ +struct test_struct: + x: string = "value_x" + y: string = "value_y" + +global foo: test_struct with: + keep(it.UNKNOWN == "override_x") +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + param = model._ModelElement__children[1] + values = param.get_resolved_value() # parse should already fail! + + def test_parameter_to_dict_two_level(self): + scenario_content = """ +struct base_struct: + x: string = "base_x" + y: string = "base_y" + +struct test_struct inherits base_struct: + z: string = "value_z" + +scenario test: + foo: test_struct with: + keep(it.x == "override_x") +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + param = model._ModelElement__children[2]._ModelElement__children[0] + values = param.get_resolved_value() + self.assertEqual({"x": "override_x", "y": "base_y", "z": "value_z"}, values) + + def test_parameter_struct_in_struct(self): + scenario_content = """ +struct base_struct: + a: string = "base_a" + b: string = "base_b" + +struct test_struct: + x: string = "value_x" + y: base_struct +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + base_struct = model._ModelElement__children[0] + + values = base_struct.get_resolved_value() + self.assertEqual({"a": "base_a", "b": "base_b"}, values) + test_struct = model._ModelElement__children[1] + values = test_struct.get_resolved_value() + self.assertEqual({"x": "value_x", "y": {"a": "base_a", "b": "base_b"}}, values) + + def test_parameter_struct_in_struct_in_struct(self): + scenario_content = """ +struct base_struct: + a: string = "base_a" + +struct l1_struct: + b: string = "value_x" + c: base_struct + +struct l2_struct: + d: string = "value_x" + e: l1_struct +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + base_struct = model._ModelElement__children[0] + self.assertEqual({"a": "base_a"}, base_struct.get_resolved_value()) + l1_struct = model._ModelElement__children[1] + self.assertEqual({'b': 'value_x', 'c': {'a': 'base_a'}}, l1_struct.get_resolved_value()) + l2_struct = model._ModelElement__children[2] + self.assertEqual({'d': 'value_x', 'e': {'b': 'value_x', 'c': { + 'a': 'base_a'}}}, l2_struct.get_resolved_value()) + + def test_unknown_type(self): + scenario_content = """ +type length is SI(m: 1) +unit cm of length is SI(m: 1, factor: 0.01) + +global val1: UNKNOWN +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) + + def test_struct_param_default(self): + scenario_content = """ +struct test_struct: + param1: string = "val1" + param2: string = "val2" + +global test_struct1: test_struct = test_struct(param2: 'OVERRIDE') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + test_struct1 = model._ModelElement__children[1] + self.assertEqual({'param1': 'val1', 'param2': 'OVERRIDE'}, + test_struct1.get_resolved_value()) + + def test_struct_param_function_wrong_type(self): + scenario_content = """ +struct other_type: + param2: string = "val2" + +struct test_struct: + param1: string = "val1" + param2: string = "val2" + +global test_struct1: test_struct = other_type(param2: 'OVERRIDE') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_struct_assigned_empty(self): + scenario_content = """ +struct base_struct: + base_param1: string = "base1" + +struct test_struct: + param_base_struct: base_struct = base_struct() +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + test_struct = model._ModelElement__children[1] + self.assertEqual({'param_base_struct': {'base_param1': 'base1'}}, + test_struct.get_resolved_value()) + + def test_struct_assigned_with_params(self): + scenario_content = """ +struct base_struct: + base_param1: string = "base1" + +struct test_struct: + param_base_struct: base_struct = base_struct('OVERRIDE') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + test_struct = model._ModelElement__children[1] + self.assertEqual({'param_base_struct': {'base_param1': 'OVERRIDE'}}, + test_struct.get_resolved_value()) + + def test_struct_assigned_by_var(self): + scenario_content = """ +struct base_struct: + base_param1: string = "base1" + +global test_struct1: base_struct = base_struct(base_param1: 'OVERRIDE') + +struct test_struct: + param_base_struct: base_struct = test_struct1 +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + test_struct = model._ModelElement__children[2] + self.assertEqual({'param_base_struct': {'base_param1': 'OVERRIDE'}}, + test_struct.get_resolved_value()) diff --git a/scenario_execution_base/test/test_osc2_parser_physical_type.py b/scenario_execution_base/test/test_osc2_parser_physical_type.py new file mode 100644 index 00000000..1643d7cc --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_physical_type.py @@ -0,0 +1,65 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test physical type parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_success(self): + scenario_content = """ + +type length is SI(m: 1) +unit m of length is SI(m: 1, factor: 1) +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) + +action odometry_distance_traveled: + distance: length = 3m +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + action = model._ModelElement__children[4].get_resolved_value() + self.assertEqual({'distance': 3.0}, action) + + def test_wrong_type(self): + scenario_content = """ + +type length is SI(m: 1) +unit m of length is SI(m: 1, factor: 1) +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) + +action odometry_distance_traveled: + distance: length = 3s +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) diff --git a/scenario_execution_base/test/test_osc2_parser_si.py b/scenario_execution_base/test/test_osc2_parser_si.py new file mode 100644 index 00000000..21781cdc --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_si.py @@ -0,0 +1,81 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test si parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_si_invalid_type(self): + scenario_content = """ +type length is SI(m: 1) +unit cm of length is SI(m: 1, factor: 0.01) + +global val1: string = 3.2cm +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) + + def test_si(self): + scenario_content = """ +type length is SI(m: 1) +unit cm of length is SI(m: 1, factor: 0.01) + +global val1: length = 3.2cm +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + param = model._ModelElement__children[2] + self.assertEqual(param.get_resolved_value(), 0.032) + + def test_si_naming(self): + scenario_content = """ +type length is SI(m: 1) +unit m of length is SI(m: 1, factor: 1) +global val1: length = 3.2m +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + param = model._ModelElement__children[2] + self.assertEqual(param.get_resolved_value(), 3.2) + + def test_si_unknown_type(self): + scenario_content = """ +type length is SI(m: 1) +unit m of UNKNOWN is SI(m: 1, factor: 1) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc") + self.assertIsNone(model) diff --git a/scenario_execution_base/test/test_osc2_parser_struct.py b/scenario_execution_base/test/test_osc2_parser_struct.py new file mode 100644 index 00000000..04967960 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_struct.py @@ -0,0 +1,178 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test struct parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_all_struct_def(self): + scenario_content = """ +global level1: string = "hey!" +global level2: string = level1 +global override: string = "override" + +struct base_struct: + base_param1: string = level2 + +struct struct1a: + struct_param: base_struct + +struct struct1b: + struct_param: base_struct = base_struct() + +struct struct1c: + struct_param: base_struct = base_struct + +struct struct2a: + struct_param: base_struct = base_struct(base_param1: "override") + +struct struct2b: + struct_param: base_struct = base_struct(base_param1: override) + +struct struct2c: + struct_param: base_struct = base_struct("override") + +struct struct2d: + struct_param: base_struct = base_struct(override) + +scenario test: + struct2e: struct1a with: + keep(it.struct_param == base_struct('override')) + + struct2f: struct1a with: + keep(it.struct_param == base_struct(base_param1: 'override')) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + struct1a = model._ModelElement__children[4].get_resolved_value() + struct1b = model._ModelElement__children[5].get_resolved_value() + struct1c = model._ModelElement__children[6].get_resolved_value() + struct2a = model._ModelElement__children[7].get_resolved_value() + struct2b = model._ModelElement__children[8].get_resolved_value() + struct2c = model._ModelElement__children[9].get_resolved_value() + struct2d = model._ModelElement__children[10].get_resolved_value() + struct2e = model._ModelElement__children[11]._ModelElement__children[0].get_resolved_value() + struct2f = model._ModelElement__children[11]._ModelElement__children[1].get_resolved_value() + self.assertEqual({'struct_param': {'base_param1': 'hey!'}}, struct1a) + self.assertEqual({'struct_param': {'base_param1': 'hey!'}}, struct1b) + self.assertEqual({'struct_param': {'base_param1': 'hey!'}}, struct1c) + self.assertEqual({'struct_param': {'base_param1': 'override'}}, struct2a) + self.assertEqual({'struct_param': {'base_param1': 'override'}}, struct2b) + self.assertEqual({'struct_param': {'base_param1': 'override'}}, struct2c) + self.assertEqual({'struct_param': {'base_param1': 'override'}}, struct2d) + self.assertEqual({'struct_param': {'base_param1': 'override'}}, struct2e) + self.assertEqual({'struct_param': {'base_param1': 'override'}}, struct2f) + + def test_multi_layer(self): + scenario_content = """ +struct base_struct: + param1: string = 'test' + +struct l1_struct: + base: base_struct + +struct l2_struct: + l1: l1_struct + +scenario test: + struct1: l2_struct with: + keep(it.l1 == l1_struct(base: base_struct)) + + struct2: l2_struct with: + keep(it.l1 == l1_struct(base: base_struct('override'))) + + struct3: l2_struct with: + keep(it.l1 == l1_struct(base_struct('override'))) + + struct4: l2_struct with: + keep(it.l1.base.param1 == 'override') + + struct5: l2_struct with: + keep(it.l1.base == base_struct('override')) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + struct1 = model._ModelElement__children[3]._ModelElement__children[0].get_resolved_value() + struct2 = model._ModelElement__children[3]._ModelElement__children[1].get_resolved_value() + struct3 = model._ModelElement__children[3]._ModelElement__children[2].get_resolved_value() + struct4 = model._ModelElement__children[3]._ModelElement__children[3].get_resolved_value() + struct5 = model._ModelElement__children[3]._ModelElement__children[4].get_resolved_value() + self.assertEqual({'l1': {'base': {'param1': 'test'}}}, struct1) + self.assertEqual({'l1': {'base': {'param1': 'override'}}}, struct2) + self.assertEqual({'l1': {'base': {'param1': 'override'}}}, struct3) + self.assertEqual({'l1': {'base': {'param1': 'override'}}}, struct4) + self.assertEqual({'l1': {'base': {'param1': 'override'}}}, struct5) + + def test_sub_struct_assignment(self): + scenario_content = """ +struct base_struct: + param1: string = 'test' + +struct l1_struct: + base: base_struct + +struct l2_struct: + l1: l1_struct + +scenario test: + struct4: l2_struct with: + keep(it.l1.base.param1 == 'override') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + struct1 = model._ModelElement__children[3]._ModelElement__children[0].get_resolved_value() + self.assertEqual({'l1': {'base': {'param1': 'override'}}}, struct1) + + def test_sub_struct_invalid_membername(self): + scenario_content = """ +struct base_struct: + param1: string = 'test' + +struct l1_struct: + base: base_struct + +struct l2_struct: + l1: l1_struct + +scenario test: + struct4: l2_struct with: + keep(it.l1.UNKNOWN.param1 == 'override') +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNone(model) diff --git a/scenario_execution_base/test/test_osc2_parser_wait.py b/scenario_execution_base/test/test_osc2_parser_wait.py new file mode 100644 index 00000000..cb5dbf3a --- /dev/null +++ b/scenario_execution_base/test/test_osc2_parser_wait.py @@ -0,0 +1,93 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test wait parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_wait_success(self): + scenario_content = """ +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) + +scenario test: + do serial: + wait elapsed(1s) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + model = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(model) + + def test_wait_invalid(self): + scenario_content = """ +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) + +scenario test: + do serial: + wait(1s) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNone(scenarios) + + def test_wait_invalid_literal(self): + scenario_content = """ +scenario test: + do serial: + wait(1) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + model = create_py_tree(model, self.parser.logger) + self.assertIsNone(model) + + def test_wait_invalid_literal2(self): + scenario_content = """ +scenario test: + do serial: + wait elapsed(1) +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + + model = create_py_tree(model, self.parser.logger) + self.assertIsNone(model) diff --git a/scenario_execution_base/test/test_osc2_standard_osc.py b/scenario_execution_base/test/test_osc2_standard_osc.py new file mode 100644 index 00000000..0ffdd670 --- /dev/null +++ b/scenario_execution_base/test/test_osc2_standard_osc.py @@ -0,0 +1,40 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test standard.osc parsing +""" +import unittest + +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + + def test_standard_osc(self): + scenario_content = """ +import osc.standard +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) diff --git a/scenario_execution_base/test/test_run_external_process.py b/scenario_execution_base/test/test_run_external_process.py new file mode 100644 index 00000000..f087e3bf --- /dev/null +++ b/scenario_execution_base/test/test_run_external_process.py @@ -0,0 +1,89 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import unittest +import rclpy +from scenario_execution import ROSScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestScenarioExecutionSuccess(unittest.TestCase): + # pylint: disable=missing-function-docstring + + @classmethod + def setUpClass(cls): + rclpy.init() + + @classmethod + def tearDownClass(cls): + rclpy.shutdown() + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ROSScenarioExecution() + + def test_failure(self): + scenario_content = """ +import osc.standard +import osc.helpers + +scenario test_run_external_process: + do parallel: + serial: + run_external_process() with: + keep(it.command == 'false') + emit end + time_out: serial: + wait elapsed(10s) + time_out_shutdown: emit fail +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertFalse(ret) + + def test_success(self): + scenario_content = """ +import osc.standard +import osc.helpers + +scenario test_run_external_process: + do parallel: + serial: + run_external_process() with: + keep(it.command == 'true') + emit end + time_out: serial: + wait elapsed(10s) + time_out_shutdown: emit fail +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) diff --git a/scenario_execution_base/test/test_scenario_events.py b/scenario_execution_base/test/test_scenario_events.py new file mode 100644 index 00000000..7fa627fc --- /dev/null +++ b/scenario_execution_base/test/test_scenario_events.py @@ -0,0 +1,92 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test predefined events +""" +import unittest +from scenario_execution_base import ScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ScenarioExecution(debug=True, log_model=True, live_tree=True, scenario='test.osc', test_output="") + + def test_failure(self): + scenario_content = """ +scenario test: + do serial: + emit fail +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertFalse(ret) + + def test_success(self): + scenario_content = """ +scenario test: + do serial: + emit end +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) + + def test_wait_for_event(self): + scenario_content = """ +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) + +scenario test: + event test + do parallel: + serial: + wait elapsed(1s) + emit test + wait elapsed(10s) + emit fail + serial: + wait @test + emit end +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) diff --git a/scenario_execution_base/test/test_scenario_oneof.py b/scenario_execution_base/test/test_scenario_oneof.py new file mode 100644 index 00000000..466d48dc --- /dev/null +++ b/scenario_execution_base/test/test_scenario_oneof.py @@ -0,0 +1,63 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test oneof parsing +""" +import unittest +from datetime import datetime + +from scenario_execution_base.scenario_execution import ScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ScenarioExecution(debug=True, log_model=True, live_tree=True, scenario='test.osc', test_output="") + + def test_oneof(self): + scenario_content = """ +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) + +scenario test: + do serial: + one_of: + wait elapsed(120s) + wait elapsed(5s) + emit end +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + + start_time = datetime.now() + ret = self.scenario_execution.run() + end_time = datetime.now() + self.assertTrue(ret) + + delta = end_time - start_time + self.assertLess(delta.total_seconds(), 15.) diff --git a/scenario_execution_base/test/test_scenario_parallel.py b/scenario_execution_base/test/test_scenario_parallel.py new file mode 100644 index 00000000..bc4ac96f --- /dev/null +++ b/scenario_execution_base/test/test_scenario_parallel.py @@ -0,0 +1,63 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Test parallel parsing +""" +import unittest + +from scenario_execution_base.scenario_execution import ScenarioExecution +from scenario_execution_base.model.osc2_parser import OpenScenario2Parser +from scenario_execution_base.model.model_to_py_tree import create_py_tree +from scenario_execution_base.utils.logging import Logger +from antlr4.InputStream import InputStream + + +class TestOSC2Parser(unittest.TestCase): + # pylint: disable=missing-function-docstring, protected-access, no-member, unused-variable + """ + Unit test for osc2_parser + """ + + def setUp(self) -> None: + self.parser = OpenScenario2Parser(Logger('test')) + self.scenario_execution = ScenarioExecution(debug=True, log_model=True, live_tree=True, scenario='test.osc', test_output="") + + def test_parallel(self): + scenario_content = """ +type time is SI(s: 1) +unit s of time is SI(s: 1, factor: 1) + +scenario test: + do serial: + parallel: + serial: + wait elapsed(5s) + emit end + serial: + wait elapsed(1s) + wait elapsed(1s) + emit fail +""" + parsed_tree, errors = self.parser.parse_input_stream(InputStream(scenario_content)) + self.assertEqual(errors, 0) + model = self.parser.create_internal_model(parsed_tree, "test.osc", True) + self.assertIsNotNone(model) + scenarios = create_py_tree(model, self.parser.logger) + self.assertIsNotNone(scenarios) + self.scenario_execution.scenarios = scenarios + ret = self.scenario_execution.run() + self.assertTrue(ret) diff --git a/scenario_execution_control/CMakeLists.txt b/scenario_execution_control/CMakeLists.txt new file mode 100644 index 00000000..973ed60f --- /dev/null +++ b/scenario_execution_control/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8.3) +project(scenario_execution_control) + +find_package(ament_cmake REQUIRED) +find_package(rclpy REQUIRED) +ament_export_dependencies(rclpy) + +# Install launch files. +install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}/) + +ament_package() diff --git a/scenario_execution_control/README.md b/scenario_execution_control/README.md new file mode 100644 index 00000000..0238390f --- /dev/null +++ b/scenario_execution_control/README.md @@ -0,0 +1,4 @@ +# Scenario Execution Control + +The `scenario_execution_control` package provides code to control scenarios (in ROS2) from another application such as RViz. + diff --git a/scenario_execution_control/launch/scenario_execution_control_launch.py b/scenario_execution_control/launch/scenario_execution_control_launch.py new file mode 100644 index 00000000..647086c1 --- /dev/null +++ b/scenario_execution_control/launch/scenario_execution_control_launch.py @@ -0,0 +1,50 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import launch +import launch_ros.actions + + +def generate_launch_description(): + ld = launch.LaunchDescription([ + + launch.actions.DeclareLaunchArgument( + 'scenario_dir', description='Directory containing osc2 scenario files'), + + launch_ros.actions.Node( + package='scenario_execution_control', + executable='scenario_execution_control', + name='scenario_execution_control', + output='screen', + emulate_tty='True', + on_exit=launch.actions.Shutdown(), + ), + launch_ros.actions.Node( + package='scenario_execution_control', + executable='scenario_list_publisher', + name='scenario_list_publisher', + output='screen', + on_exit=launch.actions.Shutdown(), + parameters=[ + {'directory': launch.substitutions.LaunchConfiguration('scenario_dir')}, + ] + ) + ]) + return ld + + +if __name__ == '__main__': + generate_launch_description() diff --git a/scenario_execution_control/package.xml b/scenario_execution_control/package.xml new file mode 100644 index 00000000..f0d48cb5 --- /dev/null +++ b/scenario_execution_control/package.xml @@ -0,0 +1,19 @@ + + + scenario_execution_control + 1.0.0 + Scenario Execution Control + Intel Labs + Intel Labs + Apache-2.0 + + scenario_execution_interfaces + std_srvs + scenario_execution + + rclpy + + + ament_python + + diff --git a/scenario_execution_control/resource/scenario_execution_control b/scenario_execution_control/resource/scenario_execution_control new file mode 100644 index 00000000..e69de29b diff --git a/scenario_execution_control/setup.cfg b/scenario_execution_control/setup.cfg new file mode 100644 index 00000000..af6d2832 --- /dev/null +++ b/scenario_execution_control/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/scenario_execution_control +[install] +install_scripts=$base/lib/scenario_execution_control \ No newline at end of file diff --git a/scenario_execution_control/setup.py b/scenario_execution_control/setup.py new file mode 100644 index 00000000..8bda59b4 --- /dev/null +++ b/scenario_execution_control/setup.py @@ -0,0 +1,48 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Setup for scenario_execution_control +""" +import os +from glob import glob + +from setuptools import setup + +PACKAGE_NAME = 'scenario_execution_control' +setup( + name=PACKAGE_NAME, + version='1.0.0', + packages=[PACKAGE_NAME], + data_files=[ + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), + (os.path.join('share', PACKAGE_NAME), ['package.xml']), + (os.path.join('share', PACKAGE_NAME), glob('launch/*launch.py')) + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Scenario Execution Control', + license='Apache License 2.0', + entry_points={ + 'console_scripts': [ + 'scenario_execution_control = scenario_execution_control.scenario_execution_control_node:main', + 'scenario_list_publisher = scenario_execution_control.scenario_list_publisher:main' + ], + }, + package_dir={'': 'src'}, +) diff --git a/scenario_execution_control/src/scenario_execution_control/__init__.py b/scenario_execution_control/src/scenario_execution_control/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_execution_control/src/scenario_execution_control/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_execution_control/src/scenario_execution_control/application_runner.py b/scenario_execution_control/src/scenario_execution_control/application_runner.py new file mode 100755 index 00000000..2b2c5bd9 --- /dev/null +++ b/scenario_execution_control/src/scenario_execution_control/application_runner.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +""" +Execute an application. +""" +from enum import Enum +from threading import Thread, Event +from datetime import datetime, timedelta +import pexpect # pylint: disable=import-error + + +class ApplicationStatus(Enum): + """ + States of an application + """ + STOPPED = 0 + STARTING = 1 + RUNNING = 2 + SHUTTINGDOWN = 3 + ERROR = 4 + + +class ApplicationRunner(object): + + """ + Execute application + """ + + def __init__(self, status_updated_fct, log_fct, ready_string=""): + """ + Constructor + """ + self._app_thread = None + self._status_updated_fct = status_updated_fct + self._log_fct = log_fct + self._shutdown_requested_event = Event() + self._ready_string = ready_string + + def execute(self, cmdline, env=None, cwd=None): + """ + Starts a thread to execute the application + """ + if self.is_running(): + self._log_fct("Application already running!") + return False + + self._shutdown_requested_event.clear() + self._app_thread = Thread(target=self.start_and_run, args=(cmdline, + env, + cwd, + self._shutdown_requested_event, + self._ready_string, + self._status_updated_fct, + self._log_fct,)) + self._app_thread.start() + + return True + + def start_and_run(self, cmdline, env, cwd, shutdown_requested_event, ready_string, # pylint: disable=too-many-arguments + status_updated_fct, log_fct): + """ + thread function + """ + status_updated_fct(ApplicationStatus.STARTING) + try: + process = self.start_process(cmdline, log_fct, env=env, cwd=cwd) + self.run(process, shutdown_requested_event, ready_string, status_updated_fct, log_fct) + except (KeyError, pexpect.ExceptionPexpect) as e: + self._log_fct("Error while starting process: {}".format(e)) + status_updated_fct(ApplicationStatus.ERROR) + + def is_running(self): + """ + returns if the application is still running + """ + if self._app_thread is None: + return False + + return self._app_thread.is_alive() + + def shutdown(self): + """ + Shut down the application thread + """ + if not self.is_running(): + return + self._log_fct("Requesting shutdown...") + self._status_updated_fct(ApplicationStatus.SHUTTINGDOWN) + self._shutdown_requested_event.set() + if self._app_thread: + self._app_thread.join() + self._log_fct("Shutdown finished.") + + def start_process(self, argument_list, log_fct, env=None, cwd=None): # pylint: disable=no-self-use + """ + Starts a process. + """ + if not argument_list: + raise KeyError("No arguments given!") + if not isinstance(argument_list, str): + executable = " ".join(argument_list) + else: + executable = argument_list + + log_fct("Executing: " + executable) + process = pexpect.spawn(executable, env=env, cwd=cwd, encoding='utf-8') + # process.logfile_read = sys.stdout + + return process + + def run(self, process, shutdown_requested_event, ready_string, status_updated_fct, log_fct): # pylint: disable=no-self-use,too-many-arguments + """ + Threaded application execution + + :return: + """ + shutting_down_trigger_time = None + signaled_running = False + while True: + if shutdown_requested_event.is_set(): + if shutting_down_trigger_time is None: + shutting_down_trigger_time = datetime.now() + log_fct("Shutdown requested while process is still running. Sending SIGHUP/SIGINT...") + process.terminate(force=False) + else: + if (datetime.now() - shutting_down_trigger_time) > timedelta(seconds=8): + log_fct("Waited 8s for application to exit. Forcing Shutdown. \ + Sending SIGKILL") + process.terminate(force=True) + try: + process.expect(".*\n", timeout=0.1) + log_fct(process.after.strip()) + if not signaled_running: + if str(process.after).find(ready_string) != -1: + status_updated_fct(ApplicationStatus.RUNNING) + log_fct("Application is ready.") + signaled_running = True + except pexpect.EOF: + # application exited + log_fct(process.before.strip()) + log_fct("Application exited. Exiting run loop") + break + except pexpect.TIMEOUT: + # no output received + pass + + process.close() + if process.exitstatus == 0: + status_updated_fct(ApplicationStatus.STOPPED) + else: + status_updated_fct(ApplicationStatus.ERROR) diff --git a/scenario_execution_control/src/scenario_execution_control/scenario_execution_control_node.py b/scenario_execution_control/src/scenario_execution_control/scenario_execution_control_node.py new file mode 100755 index 00000000..dbc18884 --- /dev/null +++ b/scenario_execution_control/src/scenario_execution_control/scenario_execution_control_node.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Execute scenarios via ros service +""" + +import os +import rclpy +from rclpy.node import Node +from rclpy.qos import QoSProfile, DurabilityPolicy + +from scenario_execution_control.application_runner import ApplicationStatus # pylint: disable=relative-import +from scenario_execution_control.scenario_execution_runner import ScenarioExecutionRunner # pylint: disable=relative-import + +from std_srvs.srv import Empty +from scenario_execution_interfaces.srv import ExecuteScenario +from scenario_execution_interfaces.msg import ScenarioExecutionStatus + + +class ScenarioExecutionControl(Node): + """ + Execute scenarios via ros service + """ + + def __init__(self): + """ + Constructor + """ + super(ScenarioExecutionControl, self).__init__('scenario_execution_control') + self.shutdown_requested = False + self._status_publisher = self.create_publisher( + ScenarioExecutionStatus, + "/scenario_execution_control/status", + qos_profile=QoSProfile(depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL)) + self.scenario_execution_status_updated(ApplicationStatus.STOPPED) + self._scenario_execution = ScenarioExecutionRunner( + self.scenario_execution_status_updated, + self.scenario_execution_log) + self._execute_scenario_service = self.create_service( + ExecuteScenario, + '/scenario_execution_control/execute_scenario', + self.execute_scenario) + self._stop_scenario_service = self.create_service( + Empty, + '/scenario_execution_control/stop_scenario', + self.stop_scenario) + + def scenario_execution_log(self, log): # pylint: disable=no-self-use + """ + Callback for application logs + """ + self.get_logger().warn(f"[SC]{log}") + + def scenario_execution_status_updated(self, status): + """ + Executed from application runner whenever the status changed + """ + self.get_logger().info(f"Status updated to {status}") + val = ScenarioExecutionStatus.STOPPED + if status == ApplicationStatus.STOPPED: + val = ScenarioExecutionStatus.STOPPED + elif status == ApplicationStatus.STARTING: + val = ScenarioExecutionStatus.STARTING + elif status == ApplicationStatus.RUNNING: + val = ScenarioExecutionStatus.RUNNING + elif status == ApplicationStatus.SHUTTINGDOWN: + val = ScenarioExecutionStatus.SHUTTINGDOWN + else: + if self.shutdown_requested: + val = ScenarioExecutionStatus.STOPPED + else: + val = ScenarioExecutionStatus.ERROR + status = ScenarioExecutionStatus() + status.status = val + self._status_publisher.publish(status) + + def execute_scenario(self, req, response=None): + """ + Execute a scenario + """ + self.get_logger().info(f"Scenario Execution requested ({req.scenario.scenario_file})...") + + response = ExecuteScenario.Response() + response.result = True + if not os.path.isfile(req.scenario.scenario_file): + self.get_logger().warn( + f"Requested scenario file not existing {req.scenario.scenario_file}") + response.result = False + else: + self.executor.create_task(self.run, req.scenario) + return response + + def stop_scenario(self, _, response=None): + """ + Stop current scenario + """ + response = Empty.Response() + + if self._scenario_execution.is_running(): + self.get_logger().info(f"Scenario Stop requested...") + self.shutdown_requested = True + self._scenario_execution.shutdown() + self.get_logger().info("Scenario Execution stopped.") + else: + self.get_logger().info("Scenario Stop requested, but not Scenario running.") + return response + + def run(self, task): + current_req = task + if self._scenario_execution.is_running(): + self.get_logger().info("Scenario Execution currently running. Shutting down.") + self._scenario_execution.shutdown() + self.get_logger().info("Scenario Execution stopped.") + self.get_logger().info(f"Executing scenario ({current_req.scenario_file})...") + + # execute scenario + self.shutdown_requested = False + scenario_executed = self._scenario_execution.execute_scenario(current_req.scenario_file) + if not scenario_executed: + self.get_logger().warn("Unable to execute scenario.") + + +def main(args=None): + """ + + main function + + :return: + """ + rclpy.init(args=args) + scenario_execution_control = ScenarioExecutionControl() + + executor = rclpy.executors.MultiThreadedExecutor() + executor.add_node(scenario_execution_control) + + try: + executor.spin() + except KeyboardInterrupt: + scenario_execution_control.get_logger().info("User requested shut down.") + finally: + if scenario_execution_control._scenario_execution.is_running(): # pylint: disable=protected-access + scenario_execution_control.get_logger().info("Scenario Execution still running. Shutting down.") + scenario_execution_control._scenario_execution.shutdown() # pylint: disable=protected-access + del scenario_execution_control + + rclpy.shutdown() + + +if __name__ == "__main__": + main() diff --git a/scenario_execution_control/src/scenario_execution_control/scenario_execution_runner.py b/scenario_execution_control/src/scenario_execution_control/scenario_execution_runner.py new file mode 100755 index 00000000..38013745 --- /dev/null +++ b/scenario_execution_control/src/scenario_execution_control/scenario_execution_runner.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Run scenario execution +""" +import os + +from scenario_execution_control.application_runner import ApplicationRunner # pylint: disable=relative-import + + +class ScenarioExecutionRunner(ApplicationRunner): + """ + Executes scenario execution + """ + + def __init__(self, status_updated_fct, log_fct): # pylint: disable=too-many-arguments + super(ScenarioExecutionRunner, self).__init__( + status_updated_fct, + log_fct, + "Executing scenario ") + + def execute_scenario(self, scenario_file): + """ + Executes scenario + """ + cmdline = ["ros2", "run", "scenario_execution", "scenario_execution", scenario_file] + + return self.execute(cmdline, env=os.environ) diff --git a/scenario_execution_control/src/scenario_execution_control/scenario_list_publisher.py b/scenario_execution_control/src/scenario_execution_control/scenario_list_publisher.py new file mode 100755 index 00000000..b5845d8e --- /dev/null +++ b/scenario_execution_control/src/scenario_execution_control/scenario_list_publisher.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +import os +import rclpy +from rclpy.qos import QoSProfile, QoSDurabilityPolicy, QoSHistoryPolicy, QoSReliabilityPolicy +from rclpy.node import Node + +from scenario_execution_interfaces.msg import ScenarioList, Scenario + + +class ScenarioListPublisher(Node): + + def __init__(self): + super().__init__('scenario_list_publisher') + + transient_local_qos = QoSProfile( + durability=QoSDurabilityPolicy.TRANSIENT_LOCAL, + reliability=QoSReliabilityPolicy.RELIABLE, + history=QoSHistoryPolicy.KEEP_LAST, + depth=1) + self.publisher = self.create_publisher( + ScenarioList, '/scenario_execution_control/available_scenarios', transient_local_qos) + + self.declare_parameter('directory', '.') + scenario_dir = self.get_parameter('directory').value + + scenarios = ScenarioList() + for f in os.listdir(scenario_dir): + path = os.path.join(scenario_dir, f) + if os.path.isfile(path) and path.endswith('.osc'): + name = os.path.splitext(os.path.basename(path))[0] + scenarios.scenarios.append(Scenario(name=name, scenario_file=path)) + self.publisher.publish(scenarios) + + +def main(args=None): + rclpy.init(args=args) + + node = ScenarioListPublisher() + + try: + rclpy.spin(node) + except KeyboardInterrupt: + pass + + node.destroy_node() + # rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/scenario_execution_gazebo/README.md b/scenario_execution_gazebo/README.md new file mode 100644 index 00000000..6580cd49 --- /dev/null +++ b/scenario_execution_gazebo/README.md @@ -0,0 +1,8 @@ +# Scenario Execution Library for Gazebo + +The `scenario_execution_gazebo` package provides actions to interact with the Gazebo simulator. + +It provides the following scenario execution library: + +- `gazebo.osc`: Gazebo specific actions to interact with the simulation + diff --git a/scenario_execution_gazebo/package.xml b/scenario_execution_gazebo/package.xml new file mode 100644 index 00000000..eaeeab03 --- /dev/null +++ b/scenario_execution_gazebo/package.xml @@ -0,0 +1,28 @@ + + + + scenario_execution_gazebo + 1.0.0 + Scenario Execution library for Gazebo + Intel Labs + Intel Labs + Apache-2.0 + + scenario_execution + + rclpy + py_trees + nav2_bringup + turtlebot4_navigation + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + ignition-fortress + + + ament_python + + diff --git a/scenario_execution_gazebo/resource/scenario_execution_gazebo b/scenario_execution_gazebo/resource/scenario_execution_gazebo new file mode 100644 index 00000000..e69de29b diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/__init__.py b/scenario_execution_gazebo/scenario_execution_gazebo/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/actions/__init__.py b/scenario_execution_gazebo/scenario_execution_gazebo/actions/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/actions/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_actor_exists.py b/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_actor_exists.py new file mode 100644 index 00000000..64cc294d --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_actor_exists.py @@ -0,0 +1,99 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from scenario_execution_base.actions import RunExternalProcess + +import py_trees +from enum import Enum +import json + + +class ActorExistsActionState(Enum): + """ + States for executing a entity check in ignition + """ + IDLE = 1 + WAITING_FOR_ACTOR = 2 + DONE = 3 + FAILURE = 4 + + +class GazeboActorExists(RunExternalProcess): + """ + Class to check existance of an entity in Ignition + + """ + + def __init__(self, entity_name: str, world_name: str): + """ + init + """ + super().__init__('GazeboActorExistsAction') + self.entity_name = entity_name + self.node = None + self.set_command(["ign", "topic", "-t", "/world/" + + world_name + "/pose/info", "-e", "--json-output"]) + self.current_state = ActorExistsActionState.IDLE + + def on_executed(self): + """ + Hook when process gets executed + """ + self.feedback_message = f"Waiting for entity '{self.entity_name}'" # pylint: disable= attribute-defined-outside-init + self.current_state = ActorExistsActionState.WAITING_FOR_ACTOR + + def get_logger_stdout(self): + """ + get logger for stderr messages + """ + return None + + def check_running_process(self): + """ + hook to check running process + + return: + py_trees.common.Status + """ + if self.current_state == ActorExistsActionState.WAITING_FOR_ACTOR: + while True: + try: + line = self.output.popleft() + pose_state = json.loads(line) + for pose in pose_state['pose']: + if pose['name'] == self.entity_name: + self.feedback_message = f"Found entity '{self.entity_name}'" # pylint: disable= attribute-defined-outside-init + self.current_state = ActorExistsActionState.DONE + return py_trees.common.Status.SUCCESS + except IndexError: + break + return py_trees.common.Status.RUNNING + else: + self.current_state = ActorExistsActionState.FAILURE + return py_trees.common.Status.FAILURE + + def on_process_finished(self, _): + """ + check result of process + + return: + py_trees.common.Status + """ + if self.current_state == ActorExistsActionState.WAITING_FOR_ACTOR: + self.current_state = ActorExistsActionState.FAILURE + return py_trees.common.Status.FAILURE + else: + return py_trees.common.Status.INVALID diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_delete_actor.py b/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_delete_actor.py new file mode 100644 index 00000000..e9fa29ac --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_delete_actor.py @@ -0,0 +1,84 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +import py_trees +from enum import Enum + +from scenario_execution_base.actions import RunExternalProcess + + +class DeleteActionState(Enum): + """ + States for executing a delete-entity in gazebo + """ + IDLE = 1 + WAITING_FOR_RESPONSE = 2 + DONE = 3 + FAILURE = 4 + + +class GazeboDeleteActor(RunExternalProcess): + """ + Class to delete an entity in Ignition + + """ + + def __init__(self, name, entity_name: str, world_name: str): + """ + init + """ + super().__init__(name) + self.entity_name = entity_name + self.node = None + self.set_command(["ign", "service", "-s", "/world/" + world_name + "/remove", + "--reqtype", "ignition.msgs.Entity", + "--reptype", "ignition.msgs.Boolean", + "--timeout", "1000", "--req", "name: \"" + self.entity_name + "\" type: MODEL"]) + self.current_state = DeleteActionState.IDLE + + def on_executed(self): + """ + Hook when process gets executed + """ + self.current_state = DeleteActionState.WAITING_FOR_RESPONSE + + def on_process_finished(self, ret): + """ + check result of process + + return: + py_trees.common.Status + """ + if self.current_state == DeleteActionState.WAITING_FOR_RESPONSE: + if ret == 0: + while True: + try: + line = self.output.popleft() + line = line.lower() + if 'error' in line or 'timed out' in line: + self.feedback_message = f"Found error output while executing '{self.get_command()}'" # pylint: disable= attribute-defined-outside-init + self.current_state = DeleteActionState.FAILURE + return py_trees.common.Status.FAILURE + except IndexError: + break + self.current_state = DeleteActionState.DONE + return py_trees.common.Status.SUCCESS + else: + self.current_state = DeleteActionState.FAILURE + return py_trees.common.Status.FAILURE + else: + return py_trees.common.Status.INVALID diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_spawn_actor.py b/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_spawn_actor.py new file mode 100644 index 00000000..4ded795d --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_spawn_actor.py @@ -0,0 +1,190 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import subprocess # nosec B404 +from enum import Enum + +from transforms3d.taitbryan import euler2quat +from std_msgs.msg import String + +from rclpy.qos import QoSProfile, QoSDurabilityPolicy, QoSHistoryPolicy, QoSReliabilityPolicy +from rclpy.logging import get_logger +from rclpy.node import Node +import py_trees +from scenario_execution_base.actions import RunExternalProcess +from .utils import SpawnUtils + + +class SpawnActionState(Enum): + """ + States for executing a spawn-entity in gazebo + """ + WAITING_FOR_TOPIC = 1 + MODEL_AVAILABLE = 2 + WAITING_FOR_RESPONSE = 3 + DONE = 4 + FAILURE = 5 + + +class GazeboSpawnActor(RunExternalProcess): + """ + Class to spawn an entity into simulation + + """ + + def __init__(self, name, associated_actor, spawn_pose: list, world_name: str, xacro_arguments: list, model: str, **kwargs): + """ + init + """ + super().__init__(name, "") + self.entity_name = associated_actor["name"] + self.model_file = model + self.spawn_pose = spawn_pose + self.world_name = world_name + self.xacro_arguments = xacro_arguments + self.current_state = SpawnActionState.WAITING_FOR_TOPIC + self.node = None + self.logger = None + self.model_sub = None + self.sdf = None + self.utils = None + + def setup(self, **kwargs): + """ + Setup ROS2 node and model + + """ + try: + self.node: Node = kwargs['node'] + except KeyError as e: + error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format( + self.name, self.__class__.__name__) + raise KeyError(error_message) from e + + self.logger = get_logger(self.name) + self.utils = SpawnUtils(logger=self.logger) + + if self.model_file.startswith('topic://'): + transient_local_qos = QoSProfile( + durability=QoSDurabilityPolicy.TRANSIENT_LOCAL, + reliability=QoSReliabilityPolicy.RELIABLE, + history=QoSHistoryPolicy.KEEP_LAST, + depth=1) + topic = self.model_file.replace('topic://', '', 1) + self.current_state = SpawnActionState.WAITING_FOR_TOPIC + self.feedback_message = f"Waiting for model on topic {topic}" # pylint: disable= attribute-defined-outside-init + self.model_sub = self.node.create_subscription( + String, topic, self.topic_callback, transient_local_qos) + else: + sdf = self.utils.parse_model_file( + self.model_file, self.entity_name, self.xacro_arguments) + + if sdf: + self.set_command(sdf) + else: + raise ValueError(f'Invalid model specified ({self.model_file})') + + def update(self) -> py_trees.common.Status: + """ + Send request + """ + if self.current_state == SpawnActionState.WAITING_FOR_TOPIC: + return py_trees.common.Status.RUNNING + else: + return super().update() + + def on_executed(self): + """ + Hook when process gets executed + """ + self.current_state = SpawnActionState.WAITING_FOR_RESPONSE + self.feedback_message = f"Executed spawning, waiting for response..." # pylint: disable= attribute-defined-outside-init + + def cleanup(self): + """ + Cleanup on shutdown + """ + self.logger.info(f"Deleting entity '{self.entity_name}' from simulation.") + if self.current_state in [SpawnActionState.WAITING_FOR_TOPIC, SpawnActionState.MODEL_AVAILABLE]: + return + + subprocess.run(["ign", "service", "-s", "/world/" + self.world_name + "/remove", # pylint: disable=subprocess-run-check + "--reqtype", "ignition.msgs.Entity", + "--reptype", "ignition.msgs.Boolean", + "--timeout", "1000", "--req", "name: \"" + self.entity_name + "\" type: MODEL"]) + + def on_process_finished(self, ret): + """ + check result of process + + return: + py_trees.common.Status + """ + if self.current_state == SpawnActionState.WAITING_FOR_RESPONSE: + if ret == 0: + while True: + try: + line = self.output.popleft() + line = line.lower() + if 'error' in line or 'timed out' in line: + self.logger.warn(line) + self.feedback_message = f"Found error output while executing '{self.command}'" # pylint: disable= attribute-defined-outside-init + self.current_state = SpawnActionState.FAILURE + return py_trees.common.Status.FAILURE + except IndexError: + break + self.current_state = SpawnActionState.DONE + return py_trees.common.Status.SUCCESS + else: + self.current_state = SpawnActionState.FAILURE + return py_trees.common.Status.FAILURE + else: + return py_trees.common.Status.INVALID + + def set_command(self, command): + """ + Set execution command + """ + # euler2quat() requires "zyx" convention, + # while in YAML, we define as pitch-roll-yaw (xyz), since it's more intuitive. + try: + quaternion = euler2quat(self.spawn_pose["orientation"]["yaw"], + self.spawn_pose["orientation"]["roll"], + self.spawn_pose["orientation"]["pitch"]) + pose = '{ position: {' \ + f' x: {self.spawn_pose["position"]["x"]} y: {self.spawn_pose["position"]["y"]} z: {self.spawn_pose["position"]["z"]}' \ + ' } orientation: {' \ + f' w: {quaternion[0]} x: {quaternion[1]} y: {quaternion[2]} z: {quaternion[3]}' \ + ' } }' + except KeyError as e: + raise ValueError("Could not get values") from e + super().set_command(["ign", "service", "-s", "/world/" + self.world_name + "/create", + "--reqtype", "ignition.msgs.EntityFactory", + "--reptype", "ignition.msgs.Boolean", + "--timeout", "30000", "--req", "pose: " + pose + " name: \"" + self.entity_name + "\" allow_renaming: false sdf: \"" + command + "\""]) + + self.logger.info(f'Command: {" ".join(self.get_command())}') + self.current_state = SpawnActionState.MODEL_AVAILABLE + + def topic_callback(self, msg): + ''' + Callback to receive model description from topic + ''' + + self.feedback_message = f"Model received from topic." # pylint: disable= attribute-defined-outside-init + self.logger.info("Received robot_description.") + self.node.destroy_subscription(self.model_sub) + self.set_command(msg.data.replace("\"", "\\\"").replace("\n", "")) diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_wait_for_sim.py b/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_wait_for_sim.py new file mode 100644 index 00000000..097ea082 --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_wait_for_sim.py @@ -0,0 +1,72 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import py_trees +from enum import Enum + +from scenario_execution_base.actions import RunExternalProcess + + +class WaitForSimulationActionState(Enum): + """ + States for waiting for the simulation + """ + IDLE = 1 + WAITING_FOR_SIM = 2 + DONE = 3 + FAILURE = 4 + + +class GazeboWaitForSim(RunExternalProcess): + """ + Class to wait for the simulation to become active + """ + + def __init__(self, name, world_name: str, **kwargs): + self.node = None + self.world_name = world_name + command = ["ign", "topic", "-t", "/world/" + + world_name + "/clock", "-e", "--json-output", "-n", "1"] + super().__init__(name, command) + self.current_state = WaitForSimulationActionState.IDLE + + def on_executed(self): + """ + Hook when process gets executed + """ + self.feedback_message = f"Waiting for simulation of world '{self.world_name}'" # pylint: disable= attribute-defined-outside-init + self.current_state = WaitForSimulationActionState.WAITING_FOR_SIM + + def get_logger_stdout(self): + """ + get logger for stderr messages + """ + return None + + def on_process_finished(self, _): + """ + check result of process + + return: + py_trees.common.Status + """ + if self.current_state == WaitForSimulationActionState.WAITING_FOR_SIM: + self.feedback_message = f"Simulation is running" # pylint: disable= attribute-defined-outside-init + self.current_state = WaitForSimulationActionState.DONE + return py_trees.common.Status.SUCCESS + else: + self.current_state = WaitForSimulationActionState.FAILURE + return py_trees.common.Status.FAILURE diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/actions/utils.py b/scenario_execution_gazebo/scenario_execution_gazebo/actions/utils.py new file mode 100644 index 00000000..d4e0a469 --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/actions/utils.py @@ -0,0 +1,245 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import subprocess # nosec B404 +import glob + +import defusedxml.ElementTree +from ament_index_python import get_package_share_directory +from pathlib import Path + + +class SpawnUtils(object): + + """Utils such as reading/parsing xml/sdf/urdf/xacro files for spawning + actors""" + + def __init__(self, logger=None): + """Initialize SpawnUtils class + + :logger: variable for using a ROS logger inside the SpawnUtils class + + """ + self.logger = logger + self.ign_gazebo_resource_paths = [] + if "IGN_GAZEBO_RESOURCE_PATH" in os.environ: + self.ign_gazebo_resource_paths = os.environ["IGN_GAZEBO_RESOURCE_PATH"].split(':') + + def parse_model_file(self, model_file: str, entity_name: str, xacro_arguments: str) -> str: + """ + Parse model file to str: + + Args: + model_file [str]: path to the model file + + return: + str of the content of model file in urdf format + """ + if '://' in model_file: + if model_file.startswith('https://fuel'): + return '' + model_file + '' + elif model_file.startswith('file://'): + # specifying sdf_filename seems to be broken, therefore load sdf + model_file = model_file.replace('file://', '', 1) + return self.read_model_file(model_file) + elif model_file.lower().endswith('.xacro'): + return self._xacro_to_urdf( + self.get_packaged_model_file_path(model_file), + xacro_arguments, entity_name) + elif model_file.lower().endswith(('.sdf', '.urdf')): + return self._parse_xml( + self.get_packaged_model_file_path(model_file), + entity_name) + elif model_file.startswith('model://'): + # search gazebo model paths for model + model_file = model_file.replace('model://', '', 1) + for path in self.ign_gazebo_resource_paths: + if not os.path.isdir(path): + continue + subfolders = [f.path for f in os.scandir(path) if f.is_dir()] + for folder in subfolders: + basename = os.path.basename(folder) + if basename == model_file: + path = os.path.join(folder, 'model.sdf') + if self.logger is not None: + self.logger.info( + f"Found model dir for {model_file}: {folder}") + else: + print(f"Found model dir for {model_file}: {folder}") + + return self.read_model_file(path) + self.logger.error( + f"Could not find {model_file} in IGN_GAZEBO_RESOURCE_PATHS") + else: + if self.logger is not None: + self.logger.error( + f'[{entity_name}] unrecognized model file definition: {model_file}') + else: + print(f'[{entity_name}] unrecognized model file definition: {model_file}') + return None + + def _parse_xml(self, xml: str, entity_name: str) -> str: + ''' + Parse xml file to str. + + Args: + xml [str]: path to xml file + entity_name [str]: name of the spawned entity + + return: + str of content of xml file + ''' + if self.logger is not None: + self.logger.info(f'[{entity_name}] Parsing xml: [{xml}]') + else: + print(f'[{entity_name}] Parsing xml: [{xml}]') + try: + tree = defusedxml.ElementTree.parse(xml) + root = tree.getroot() + except FileNotFoundError as e: + if self.logger is not None: + self.logger.error(f'Parsing xml {entity_name} failed: {e}') + else: + print(f'Parsing xml {entity_name} failed: {e}') + return defusedxml.ElementTree.tostring(root).decode().replace("\"", "\\\"").replace("\n", "") + + @staticmethod + def get_packaged_model_file_path(model_path): + """ + get path of a packaged model file + """ + model_file = model_path.split('://') + if len(model_file) != 2: + raise ValueError( + f'scenario_execution_amr_plugin:spawn: Invalid model file path: {model_path}." \ + " Expected format ://.') + model_file_path = os.path.join(get_package_share_directory( + model_file[0]), model_file[1]) + if not os.path.exists(model_file_path): + raise ValueError( + f'scenario_execution_amr_plugin:spawn: Model file not existing: {model_file_path}.') + return model_file_path + + def _xacro_to_urdf(self, path_to_xacro, xacro_arguments, entity_name: str) -> str: + """ + function to covert the xacro file to urdf file + + Args: + path_to_xacro [str]: path to xacro file + entity_name [str]: name of the spawned entity + + return: + str of content of coverted xacro file + """ + if self.logger is not None: + self.logger.info( + f'[{entity_name}] Parsing xacro: {path_to_xacro}, args: {xacro_arguments}') + else: + print(f'[{entity_name}] Parsing xacro: {path_to_xacro}, args: {xacro_arguments}') + + arg_list = xacro_arguments.split(',') + try: + with subprocess.Popen(['xacro', path_to_xacro] + arg_list, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, text=True) as process: + stdout, stderr = process.communicate() + if stderr: + if self.logger is not None: + self.logger.warn(f'Parsing xacro {entity_name} has stderr: {stderr}') + else: + print(f'Parsing xacro {entity_name} has stderr: {stderr}') + except subprocess.CalledProcessError as e: + stdout = None + if self.logger is not None: + self.logger.error(f'Parsing xacro {entity_name} failed: {e.output}') + else: + print(f'Parsing xacro {entity_name} failed: {e.output}') + return stdout.replace("\"", "\\\"").replace("\n", "") + + def read_model_file(self, model_file): + ''' + read model from file + ''' + try: + f = open(model_file, 'r') + xml = f.read().replace("\"", "\\\"").replace("\n", "") + f.close() + return xml + except FileNotFoundError as e: + if self.logger is not None: + self.logger.error(f'Parsing xml {model_file} failed: {e}') + else: + print(f'Parsing xml {model_file} failed: {e}') + return None + + def find_mesh(self, mesh_name: str, entity_name: str) -> str: + """find the mesh given by a certain name. + + :mesh_name: Name of the mesh + :returns: file path to the mesh + + """ + if '://' in mesh_name: + if mesh_name.startswith('https://fuel'): + return mesh_name + elif mesh_name.startswith('file://'): + # make sure we have an absolute mesh filepath + try: + mesh_file = str(Path(mesh_name.replace('file://', '', 1)).resolve()) + except FileNotFoundError as e: + if self.logger is not None: + self.logger.error(f'Could not find mesh {mesh_name} for {entity_name}: {e}') + else: + print(f'Could not find mesh {mesh_name} for {entity_name}: {e}') + return mesh_file + elif mesh_name.startswith('model://'): + # search gazebo model paths for mesh + mesh_file = mesh_name.replace('model://', '', 1) + if not mesh_file.endswith('.dae'): + mesh_file += '.dae' + + for path in self.ign_gazebo_resource_paths: + if not os.path.isdir(path): + continue + for filename in glob.iglob(path + '**/**', recursive=True): + path = Path(filename) + if str(path.name) == mesh_file: + if self.logger is not None: + self.logger.info( + f"Found mesh dir for {mesh_file}: {path.parent}") + else: + print(f"Found mesh dir for {mesh_file}: {path.parent}") + return str(path.resolve()) + + self.logger.error( + f"Could not find {mesh_name} in IGN_GAZEBO_RESOURCE_PATHS") + elif mesh_name.endswith('.dae'): + try: + mesh_file = self.get_packaged_model_file_path(mesh_name) + return mesh_file + except FileNotFoundError as e: + if self.logger is not None: + self.logger.error(f'Could not find mesh {mesh_name} for {entity_name}: {e}') + else: + print(f'Could not find mesh {mesh_name} for {entity_name}: {e}') + + else: + if self.logger is not None: + self.logger.error( + f'[{entity_name}] unrecognized model file definition: {mesh_name}') + else: + print(f'[{entity_name}] unrecognized model file definition: {mesh_name}') + return None diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/get_osc_library.py b/scenario_execution_gazebo/scenario_execution_gazebo/get_osc_library.py new file mode 100644 index 00000000..6e73d6a3 --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/get_osc_library.py @@ -0,0 +1,19 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +def get_gazebo_library(): + return 'scenario_execution_gazebo', 'gazebo.osc' diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/lib_osc/gazebo.osc b/scenario_execution_gazebo/scenario_execution_gazebo/lib_osc/gazebo.osc new file mode 100644 index 00000000..26e9f2f4 --- /dev/null +++ b/scenario_execution_gazebo/scenario_execution_gazebo/lib_osc/gazebo.osc @@ -0,0 +1,22 @@ +import osc.standard +import osc.robotics + +action wait_for_sim: + # Wait for simulation to become active + world_name: string = 'default' # simulation world name + +action actor_exists: + # report success if an actor with a specific name exists + entity_name: string # name of the actor within simulation + world_name: string = 'default' # simulation world name + +action osc_object.spawn: + # Spawn a simulation entity, uses namespace of entity + spawn_pose: pose_3d # position at which the object gets spawned + world_name: string = 'default' # simulation world name + model: string # model definition + xacro_arguments: string = '' # comma-separated list of argument key:=value pairs + +action osc_object.delete: + # Delete an actor from simulation + world_name: string = 'default' # simulation world name diff --git a/scenario_execution_gazebo/setup.cfg b/scenario_execution_gazebo/setup.cfg new file mode 100644 index 00000000..05085b89 --- /dev/null +++ b/scenario_execution_gazebo/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/scenario_execution_gazebo +[install] +install_scripts=$base/lib/scenario_execution_gazebo diff --git a/scenario_execution_gazebo/setup.py b/scenario_execution_gazebo/setup.py new file mode 100644 index 00000000..4d316c6c --- /dev/null +++ b/scenario_execution_gazebo/setup.py @@ -0,0 +1,60 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Setup python package """ +from glob import glob +import os +from setuptools import setup + +PACKAGE_NAME = 'scenario_execution_gazebo' + +setup( + name=PACKAGE_NAME, + version='1.0.0', + packages=[PACKAGE_NAME], + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + PACKAGE_NAME]), + ('share/' + PACKAGE_NAME, ['package.xml']), + (os.path.join('share', PACKAGE_NAME, 'scenarios'), glob('scenarios/*.osc')), + (os.path.join('share', PACKAGE_NAME, 'launch'), glob('launch/*launch.py')), + (os.path.join('share', PACKAGE_NAME, 'params'), glob('params/*.yaml')), + (os.path.join('share', PACKAGE_NAME, 'models'), glob('models/*.sdf*')), + (os.path.join('share', PACKAGE_NAME, 'models'), glob('models/*.dae*')), + (os.path.join('share', PACKAGE_NAME, 'models'), glob('models/*.png*')), + (os.path.join('share', PACKAGE_NAME, 'worlds'), glob('worlds/*.world')), + (os.path.join('share', PACKAGE_NAME, 'worlds'), glob('worlds/*.model')) + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Scenario Execution library for Gazebo', + license='Apache License 2.0', + tests_require=['pytest'], + entry_points={ + 'scenario_execution.actions': [ + 'osc_object.spawn = scenario_execution_gazebo.actions.gazebo_spawn_actor:GazeboSpawnActor', + 'actor_exists = scenario_execution_gazebo.actions.gazebo_actor_exists:GazeboActorExists', + 'osc_object.delete = scenario_execution_gazebo.actions.gazebo_delete_actor:GazeboDeleteActor', + 'wait_for_sim = scenario_execution_gazebo.actions.gazebo_wait_for_sim:GazeboWaitForSim', + ], + 'scenario_execution.osc_libraries': [ + 'gazebo = ' + 'scenario_execution_gazebo.get_osc_library:get_gazebo_library', + ] + }, +) diff --git a/scenario_execution_interfaces/CMakeLists.txt b/scenario_execution_interfaces/CMakeLists.txt new file mode 100644 index 00000000..89e50d8e --- /dev/null +++ b/scenario_execution_interfaces/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.8) +project(scenario_execution_interfaces) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(builtin_interfaces REQUIRED) + +rosidl_generate_interfaces(${PROJECT_NAME} + msg/ScenarioStatus.msg + msg/Scenario.msg + msg/ScenarioList.msg + msg/ScenarioExecutionStatus.msg + srv/ExecuteScenario.srv + DEPENDENCIES + builtin_interfaces + ) + +ament_export_dependencies(rosidl_default_runtime) + +ament_package() diff --git a/scenario_execution_interfaces/README.md b/scenario_execution_interfaces/README.md new file mode 100644 index 00000000..e3f08c86 --- /dev/null +++ b/scenario_execution_interfaces/README.md @@ -0,0 +1,3 @@ +# Scenario Execution Interfaces + +The `scenario_execution_interfaces` package provides ROS2 messages and services, which are used to interface with the [scenario execution control package](../scenario_execution_control/README.md) diff --git a/scenario_execution_interfaces/msg/Scenario.msg b/scenario_execution_interfaces/msg/Scenario.msg new file mode 100644 index 00000000..c2626f38 --- /dev/null +++ b/scenario_execution_interfaces/msg/Scenario.msg @@ -0,0 +1,18 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +string name +string scenario_file diff --git a/scenario_execution_interfaces/msg/ScenarioExecutionStatus.msg b/scenario_execution_interfaces/msg/ScenarioExecutionStatus.msg new file mode 100644 index 00000000..fde94aab --- /dev/null +++ b/scenario_execution_interfaces/msg/ScenarioExecutionStatus.msg @@ -0,0 +1,24 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +uint8 STOPPED = 0 +uint8 STARTING = 1 +uint8 RUNNING = 2 +uint8 SHUTTINGDOWN = 3 +uint8 ERROR = 4 + +uint8 status + diff --git a/scenario_execution_interfaces/msg/ScenarioList.msg b/scenario_execution_interfaces/msg/ScenarioList.msg new file mode 100644 index 00000000..376a9e8c --- /dev/null +++ b/scenario_execution_interfaces/msg/ScenarioList.msg @@ -0,0 +1,18 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +Scenario[] scenarios + diff --git a/scenario_execution_interfaces/msg/ScenarioStatus.msg b/scenario_execution_interfaces/msg/ScenarioStatus.msg new file mode 100644 index 00000000..78ead9c3 --- /dev/null +++ b/scenario_execution_interfaces/msg/ScenarioStatus.msg @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +builtin_interfaces/Time system_time +builtin_interfaces/Time ros_time + +string data diff --git a/scenario_execution_interfaces/package.xml b/scenario_execution_interfaces/package.xml new file mode 100644 index 00000000..2eeac5c1 --- /dev/null +++ b/scenario_execution_interfaces/package.xml @@ -0,0 +1,25 @@ + + + + scenario_execution_interfaces + 1.0.0 + ROS2 Interfaces for Scenario Execution + Intel Labs + Intel Labs + Apache-2.0 + + ament_cmake + rosidl_default_generators + rosidl_default_runtime + geometry_msgs + rosidl_interface_packages + + builtin_interfaces + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/scenario_execution_interfaces/srv/ExecuteScenario.srv b/scenario_execution_interfaces/srv/ExecuteScenario.srv new file mode 100644 index 00000000..04ebb58b --- /dev/null +++ b/scenario_execution_interfaces/srv/ExecuteScenario.srv @@ -0,0 +1,4 @@ +Scenario scenario +--- +bool result + diff --git a/scenario_execution_rviz/CMakeLists.txt b/scenario_execution_rviz/CMakeLists.txt new file mode 100644 index 00000000..8a9a428f --- /dev/null +++ b/scenario_execution_rviz/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.3) +project(scenario_execution_rviz) + +set(CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD 14) + +add_compile_options(-Wall -Wextra -Werror -flto -fvisibility=hidden -z noexecstac -fPIC -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now -fstack-protector-strong) + +find_package(ament_cmake REQUIRED) +find_package(rviz_common REQUIRED) +find_package(nav_msgs COMPONENTS) +find_package(std_srvs COMPONENTS) +find_package(scenario_execution_interfaces COMPONENTS) +find_package(py_trees_ros_interfaces COMPONENTS) +find_package(pluginlib REQUIRED) +find_package(rviz_ogre_vendor REQUIRED) + +set(CMAKE_AUTOMOC ON) + +find_package(Qt5 REQUIRED Core Widgets) +set(QT_LIBRARIES Qt5::Widgets) + +set(SRC_FILES src/indicator_widget.cpp + src/control_panel.cpp) + +qt5_add_resources(SRC_FILES scenario_execution_rviz.qrc) + +add_library(${PROJECT_NAME} SHARED ${SRC_FILES}) + +target_link_libraries(${PROJECT_NAME} ${QT_LIBRARIES} + rviz_ogre_vendor::OgreMain) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +pluginlib_export_plugin_description_file(rviz_common + plugin_description.xml) + +ament_target_dependencies(scenario_execution_rviz rclcpp nav_msgs std_srvs + scenario_execution_interfaces py_trees_ros_interfaces rviz_common) + +ament_export_libraries(${PROJECT_NAME}) + +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + +install(FILES plugin_description.xml DESTINATION "share/${PROJECT_NAME}") + +install(DIRECTORY icons/ DESTINATION "share/${PROJECT_NAME}") + +ament_package() + diff --git a/scenario_execution_rviz/LICENSE_MIT b/scenario_execution_rviz/LICENSE_MIT new file mode 100644 index 00000000..7e145886 --- /dev/null +++ b/scenario_execution_rviz/LICENSE_MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Intel Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/scenario_execution_rviz/README.md b/scenario_execution_rviz/README.md new file mode 100644 index 00000000..febd4a4c --- /dev/null +++ b/scenario_execution_rviz/README.md @@ -0,0 +1,3 @@ +# Scenario Execution Rviz + +The `scenario_execution_rviz` package provides [rviz](https://github.com/ros2/rviz) plugins for visualizing and controlling the scenario when working with [ROS 2](https://docs.ros.org/en/rolling/index.html). diff --git a/scenario_execution_rviz/icons/check-o.png b/scenario_execution_rviz/icons/check-o.png new file mode 100755 index 00000000..b2982c8b Binary files /dev/null and b/scenario_execution_rviz/icons/check-o.png differ diff --git a/scenario_execution_rviz/icons/chevron-right-o.png b/scenario_execution_rviz/icons/chevron-right-o.png new file mode 100755 index 00000000..91ea8254 Binary files /dev/null and b/scenario_execution_rviz/icons/chevron-right-o.png differ diff --git a/scenario_execution_rviz/icons/close-o.png b/scenario_execution_rviz/icons/close-o.png new file mode 100755 index 00000000..97aab2ed Binary files /dev/null and b/scenario_execution_rviz/icons/close-o.png differ diff --git a/scenario_execution_rviz/icons/corner-down-right.png b/scenario_execution_rviz/icons/corner-down-right.png new file mode 100755 index 00000000..03f0a575 Binary files /dev/null and b/scenario_execution_rviz/icons/corner-down-right.png differ diff --git a/scenario_execution_rviz/icons/play.png b/scenario_execution_rviz/icons/play.png new file mode 100644 index 00000000..07078775 Binary files /dev/null and b/scenario_execution_rviz/icons/play.png differ diff --git a/scenario_execution_rviz/icons/stop.png b/scenario_execution_rviz/icons/stop.png new file mode 100644 index 00000000..50c2364e Binary files /dev/null and b/scenario_execution_rviz/icons/stop.png differ diff --git a/scenario_execution_rviz/package.xml b/scenario_execution_rviz/package.xml new file mode 100644 index 00000000..76180945 --- /dev/null +++ b/scenario_execution_rviz/package.xml @@ -0,0 +1,31 @@ + + + scenario_execution_rviz + 0.0.1 + The scenario_execution_rviz package + Intel Labs + Intel Labs + Apache-2.0 + MIT + + rclcpp + ament_cmake + rviz_common + + qtbase5-dev + nav_msgs + geometry_msgs + scenario_execution_interfaces + py_trees_ros_interfaces + nav_msgs + geometry_msgs + scenario_execution_interfaces + py_trees_ros_interfaces + libqt5-core + libqt5-gui + libqt5-widgets + + + ament_cmake + + diff --git a/scenario_execution_rviz/plugin_description.xml b/scenario_execution_rviz/plugin_description.xml new file mode 100644 index 00000000..596e8d51 --- /dev/null +++ b/scenario_execution_rviz/plugin_description.xml @@ -0,0 +1,9 @@ + + + + A panel widget for controlling scenario execution + + + diff --git a/scenario_execution_rviz/scenario_execution_rviz.qrc b/scenario_execution_rviz/scenario_execution_rviz.qrc new file mode 100644 index 00000000..4e71614f --- /dev/null +++ b/scenario_execution_rviz/scenario_execution_rviz.qrc @@ -0,0 +1,10 @@ + + + icons/play.png + icons/stop.png + icons/check-o.png + icons/chevron-right-o.png + icons/close-o.png + icons/corner-down-right.png + + \ No newline at end of file diff --git a/scenario_execution_rviz/src/control_panel.cpp b/scenario_execution_rviz/src/control_panel.cpp new file mode 100644 index 00000000..48130f73 --- /dev/null +++ b/scenario_execution_rviz/src/control_panel.cpp @@ -0,0 +1,203 @@ +// Copyright (C) 2024 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions +// and limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (c) 2020 Intel Corporation + * + * This work is licensed under the terms of the MIT license. + * For a copy, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "rviz_common/display_context.hpp" +#include +#include +#include +#include + +#include "control_panel.h" +#include "indicator_widget.h" + +using std::placeholders::_1; +namespace scenario_execution_rviz { + +ControlPanel::ControlPanel(QWidget *parent) : rviz_common::Panel(parent) { + QVBoxLayout *layout = new QVBoxLayout; + + QFormLayout *formLayout = new QFormLayout; + + QHBoxLayout *rowLayout = new QHBoxLayout; + + mScenarioSelection = new QComboBox(); + rowLayout->addWidget(mScenarioSelection, 10); + + QPixmap pixmap(":/icons/play.png"); + QIcon iconPlay(pixmap); + mTriggerScenarioButton = new QPushButton(iconPlay, ""); + rowLayout->addWidget(mTriggerScenarioButton); + + mIndicatorWidget = new IndicatorWidget(); + rowLayout->addWidget(mIndicatorWidget); + connect(mTriggerScenarioButton, SIGNAL(released()), this, + SLOT(scenarioExecuteButtonClicked())); + + formLayout->addRow(rowLayout); + + layout->addLayout(formLayout); + + setLayout(layout); + + setScenarioExecutionStatus(false); +} + +void ControlPanel::onInitialize() { + _node = getDisplayContext()->getRosNodeAbstraction().lock()->get_raw_node(); + + mExecuteScenarioClient = + _node->create_client( + "/scenario_execution_control/execute_scenario"); + mStopScenarioClient = _node->create_client( + "/scenario_execution_control/stop_scenario"); + mScenarioExecutionStatusSubscriber = _node->create_subscription< + scenario_execution_interfaces::msg::ScenarioExecutionStatus>( + "/scenario_execution_control/status", 10, + std::bind(&ControlPanel::scenarioExecutionStatusChanged, this, _1)); + rclcpp::QoS static_QoS = rclcpp::QoS(1).transient_local(); + mScenarioSubscriber = _node->create_subscription< + scenario_execution_interfaces::msg::ScenarioList>( + "/scenario_execution_control/available_scenarios", static_QoS, + std::bind(&ControlPanel::scenariosChanged, this, _1)); +} + +void ControlPanel::scenarioExecuteButtonClicked() { + if (!mScenarioIsRunning) { + for (auto const &scenario : mScenarios->scenarios) { + if (QString::fromStdString(scenario.name) == + mScenarioSelection->currentText()) { + auto request = std::make_shared< + scenario_execution_interfaces::srv::ExecuteScenario::Request>(); + request->scenario = scenario; + // Check if service is available + if (!mExecuteScenarioClient->wait_for_service( + std::chrono::seconds(1))) { + RCLCPP_WARN(rclcpp::get_logger("rclcpp"), + "Failed to call service executeScenario"); + mIndicatorWidget->setState(IndicatorWidget::State::Error); + } + auto result = mExecuteScenarioClient->async_send_request(request); + break; + } + } + } else { + if (!mStopScenarioClient->wait_for_service(std::chrono::seconds(1))) { + RCLCPP_WARN(rclcpp::get_logger("rclcpp"), + "Failed to call service stopScenario"); + mIndicatorWidget->setState(IndicatorWidget::State::Error); + } + auto request = std::make_shared(); + auto result = mStopScenarioClient->async_send_request(request); + } +} + +void ControlPanel::scenarioExecutionStatusChanged( + const scenario_execution_interfaces::msg::ScenarioExecutionStatus::SharedPtr + msg) { + bool isRunning = false; + if (msg->status == + scenario_execution_interfaces::msg::ScenarioExecutionStatus::STOPPED) { + mIndicatorWidget->setState(IndicatorWidget::State::Stopped); + } else if (msg->status == scenario_execution_interfaces::msg:: + ScenarioExecutionStatus::STARTING) { + mIndicatorWidget->setState(IndicatorWidget::State::Starting); + isRunning = true; + } else if (msg->status == scenario_execution_interfaces::msg:: + ScenarioExecutionStatus::RUNNING) { + mIndicatorWidget->setState(IndicatorWidget::State::Running); + isRunning = true; + } else if (msg->status == scenario_execution_interfaces::msg:: + ScenarioExecutionStatus::SHUTTINGDOWN) { + mIndicatorWidget->setState(IndicatorWidget::State::ShuttingDown); + isRunning = true; + } else { + mIndicatorWidget->setState(IndicatorWidget::State::Error); + } + + updateScenarioExecutionRunning(isRunning); +} + +void ControlPanel::updateScenarioExecutionRunning(bool isRunning) { + mScenarioIsRunning = isRunning; + if (isRunning) { + QPixmap pixmapStop(":/icons/stop.png"); + QIcon iconStop(pixmapStop); + mTriggerScenarioButton->setIcon(iconStop); + } else { + QPixmap pixmapPlay(":/icons/play.png"); + QIcon iconPlay(pixmapPlay); + mTriggerScenarioButton->setIcon(iconPlay); + } +} + +void ControlPanel::setScenarioExecutionStatus(bool active) { + mScenarioSelection->setEnabled(active); + mTriggerScenarioButton->setEnabled(active); + mIndicatorWidget->setEnabled(active); +} + +void ControlPanel::scenariosChanged( + const scenario_execution_interfaces::msg::ScenarioList::SharedPtr msg) { + auto currentSelection = mScenarioSelection->currentText(); + mScenarios = msg; + mScenarioSelection->clear(); + int idx = 0; + QStringList tmpScenarios; + for (auto const &scenario : msg->scenarios) { + auto name = QString::fromStdString(scenario.name); + tmpScenarios.append(name); + } + tmpScenarios.sort(Qt::CaseInsensitive); + for (auto const &scenario : tmpScenarios) { + mScenarioSelection->addItem(scenario); + if (scenario == currentSelection) { // switch to previously selected item + mScenarioSelection->setCurrentIndex(idx); + } + ++idx; + } + setScenarioExecutionStatus(mScenarioSelection->count() > 0); +} + +} // end namespace scenario_execution_rviz + +#include +PLUGINLIB_EXPORT_CLASS(scenario_execution_rviz::ControlPanel, + rviz_common::Panel) diff --git a/scenario_execution_rviz/src/control_panel.h b/scenario_execution_rviz/src/control_panel.h new file mode 100644 index 00000000..15d64c6d --- /dev/null +++ b/scenario_execution_rviz/src/control_panel.h @@ -0,0 +1,77 @@ +// Copyright (C) 2024 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions +// and limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (c) 2020 Intel Corporation + * + * This work is licensed under the terms of the MIT license. + * For a copy, see . + */ + +#pragma once + +#include "rclcpp/rclcpp.hpp" +#include "rviz_common/ros_integration/ros_node_abstraction_iface.hpp" +#include +#include +#include +#include +#include + +class QPushButton; +class QComboBox; + +namespace scenario_execution_rviz { + +class IndicatorWidget; + +class ControlPanel : public rviz_common::Panel { + Q_OBJECT +public: + ControlPanel(QWidget *parent = 0); + +protected Q_SLOTS: + void scenarioExecuteButtonClicked(); + +protected: + virtual void onInitialize() override; + void setScenarioExecutionStatus(bool active); + + void + scenarioExecutionStatusChanged(const scenario_execution_interfaces::msg:: + ScenarioExecutionStatus::SharedPtr msg); + void scenariosChanged( + const scenario_execution_interfaces::msg::ScenarioList::SharedPtr msg); + void updateScenarioExecutionRunning(bool isRunning); + + rclcpp::Node::SharedPtr _node; + + QPushButton *mTriggerScenarioButton; + QComboBox *mScenarioSelection; + IndicatorWidget *mIndicatorWidget; + rclcpp::Client::SharedPtr + mExecuteScenarioClient; + rclcpp::Client::SharedPtr mStopScenarioClient; + rclcpp::Subscription:: + SharedPtr mScenarioSubscriber; + rclcpp::Subscription< + scenario_execution_interfaces::msg::ScenarioExecutionStatus>::SharedPtr + mScenarioExecutionStatusSubscriber; + + scenario_execution_interfaces::msg::ScenarioList::SharedPtr mScenarios; + bool mScenarioIsRunning = false; +}; + +} // end namespace scenario_execution_rviz diff --git a/scenario_execution_rviz/src/indicator_widget.cpp b/scenario_execution_rviz/src/indicator_widget.cpp new file mode 100644 index 00000000..f43eebb2 --- /dev/null +++ b/scenario_execution_rviz/src/indicator_widget.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2024 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions +// and limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (c) 2020 Intel Corporation + * + * This work is licensed under the terms of the MIT license. + * For a copy, see . + */ +#include "indicator_widget.h" +#include + +namespace scenario_execution_rviz { + +IndicatorWidget::IndicatorWidget(QWidget *parent) + : QWidget(parent), mCurrentState(IndicatorWidget::State::Stopped) { + setFixedSize(18, 18); +} + +void IndicatorWidget::paintEvent(QPaintEvent *event) { + (void)event; + QPainter painter(this); + painter.setPen(Qt::darkGray); + if (mCurrentState == IndicatorWidget::State::Stopped) { + painter.setBrush(QBrush(Qt::lightGray, Qt::SolidPattern)); + } else if (mCurrentState == IndicatorWidget::State::Running) { + painter.setBrush(QBrush(Qt::green, Qt::SolidPattern)); + } else if (mCurrentState == IndicatorWidget::State::Error) { + painter.setBrush(QBrush(Qt::red, Qt::SolidPattern)); + } else if ((mCurrentState == IndicatorWidget::State::Starting) || + (mCurrentState == IndicatorWidget::State::ShuttingDown)) { + painter.setBrush(QBrush(Qt::yellow, Qt::SolidPattern)); + } else { + painter.setBrush(QBrush(Qt::black, Qt::SolidPattern)); + } + painter.drawEllipse(1, 1, 16, 16); +} + +void IndicatorWidget::setState(IndicatorWidget::State state) { + mCurrentState = state; + repaint(); +} + +} // end namespace scenario_execution_rviz diff --git a/scenario_execution_rviz/src/indicator_widget.h b/scenario_execution_rviz/src/indicator_widget.h new file mode 100644 index 00000000..03ba35e5 --- /dev/null +++ b/scenario_execution_rviz/src/indicator_widget.h @@ -0,0 +1,43 @@ +// Copyright (C) 2024 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions +// and limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (c) 2020 Intel Corporation + * + * This work is licensed under the terms of the MIT license. + * For a copy, see . + */ +#pragma once + +#include + +namespace scenario_execution_rviz { + +class IndicatorWidget : public QWidget { + Q_OBJECT +public: + enum class State { Stopped, Starting, Running, ShuttingDown, Error }; + + IndicatorWidget(QWidget *parent = 0); + + virtual void paintEvent(QPaintEvent *event) override; + + void setState(IndicatorWidget::State state); + +private: + State mCurrentState{State::Stopped}; +}; + +} // end namespace scenario_execution_rviz diff --git a/security.md b/security.md new file mode 100644 index 00000000..e70fcb0f --- /dev/null +++ b/security.md @@ -0,0 +1,6 @@ +# Security Policy +Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. + +## Reporting a Vulnerability +Please report any security vulnerabilities in this project [utilizing the guidelines here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). + diff --git a/simulation/gazebo/tb4_sim_scenario/CMakeLists.txt b/simulation/gazebo/tb4_sim_scenario/CMakeLists.txt new file mode 100644 index 00000000..ea6f2aad --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.8) +project(tb4_sim_scenario) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) + +find_package(ros_ign_interfaces REQUIRED) + +install( + DIRECTORY launch params worlds urdf + DESTINATION share/${PROJECT_NAME} +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/simulation/gazebo/tb4_sim_scenario/README.md b/simulation/gazebo/tb4_sim_scenario/README.md new file mode 100644 index 00000000..2953df1d --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/README.md @@ -0,0 +1,9 @@ +# Turtlebot4 Simulation Scenario Execution + +The `tb_sim_scenario` package provides launch files and parameter files to start the Gazebo Simulator, spawn the Turtlebot 4 and execute a scenario. + +## Launch and arguments + +```bash +ros2 launch tb4_sim_scenario_bringup sim_nav_scenario_launch.py scenario:= +``` diff --git a/simulation/gazebo/tb4_sim_scenario/launch/ignition_launch.py b/simulation/gazebo/tb4_sim_scenario/launch/ignition_launch.py new file mode 100644 index 00000000..2a11f4b6 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/launch/ignition_launch.py @@ -0,0 +1,124 @@ +# Copyright (C) 2024 Intel Corporation +# Copyright 2023 Clearpath Robotics, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @author Roni Kreinin (rkreinin@clearpathrobotics.com) + +import os + +from pathlib import Path + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, ExecuteProcess, Shutdown +from launch.actions import SetEnvironmentVariable +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution +from launch_ros.actions import Node + + +ARGUMENTS = [ + DeclareLaunchArgument('use_sim_time', default_value='true', + choices=['true', 'false'], + description='use_sim_time'), + + DeclareLaunchArgument('world', default_value='maze', + description='Ignition World'), + + DeclareLaunchArgument('headless', default_value='False', + description='Whether to execute simulation gui'), +] + + +def generate_launch_description(): + + # Directories + pkg_turtlebot4_ignition_bringup = get_package_share_directory( + 'turtlebot4_ignition_bringup') + pkg_tb4_sim_scenario = get_package_share_directory( + 'tb4_sim_scenario') + pkg_turtlebot4_ignition_gui_plugins = get_package_share_directory( + 'turtlebot4_ignition_gui_plugins') + pkg_turtlebot4_description = get_package_share_directory( + 'turtlebot4_description') + pkg_irobot_create_description = get_package_share_directory( + 'irobot_create_description') + pkg_irobot_create_ignition_bringup = get_package_share_directory( + 'irobot_create_ignition_bringup') + pkg_irobot_create_ignition_plugins = get_package_share_directory( + 'irobot_create_ignition_plugins') + + # Set ignition resource path + ign_resource_path = SetEnvironmentVariable( + name='IGN_GAZEBO_RESOURCE_PATH', + value=[ + os.path.join(pkg_turtlebot4_ignition_bringup, 'worlds'), ':' + + os.path.join(pkg_irobot_create_ignition_bringup, 'worlds'), ':' + + str(Path(pkg_turtlebot4_description).parent.resolve()), ':' + + str(Path(pkg_irobot_create_description).parent.resolve())] + ) + + ign_gui_plugin_path = SetEnvironmentVariable( + name='IGN_GUI_PLUGIN_PATH', + value=[ + os.path.join(pkg_turtlebot4_ignition_gui_plugins, 'lib'), ':' + + os.path.join(pkg_irobot_create_ignition_plugins, 'lib')]) + + env = {'GZ_SIM_SYSTEM_PLUGIN_PATH': + ':'.join([os.environ.get('GZ_SIM_SYSTEM_PLUGIN_PATH', default=''), + os.environ.get('LD_LIBRARY_PATH', default='')]), + 'IGN_GAZEBO_SYSTEM_PLUGIN_PATH': # TODO(CH3): To support pre-garden. Deprecated. + ':'.join([os.environ.get('IGN_GAZEBO_SYSTEM_PLUGIN_PATH', default=''), + os.environ.get('LD_LIBRARY_PATH', default='')])} + # Ignition gazebo + ignition_gazebo = ExecuteProcess( + cmd=['ruby', '/usr/bin/ign', 'gazebo', [PathJoinSubstitution( + [pkg_tb4_sim_scenario, 'worlds', LaunchConfiguration('world')]), '.sdf'], '-v', '4', '-r', '-s', '--force-version', '6'], + output='screen', + additional_env=env, + on_exit=Shutdown(), + sigterm_timeout='5', + sigkill_timeout='10', + log_cmd=True, + emulate_tty=True + ) + + ignition_gazebo_gui = ExecuteProcess( + cmd=['ruby', '/usr/bin/ign', 'gazebo', '-g', '-v', '4', '--gui-config', + PathJoinSubstitution([pkg_turtlebot4_ignition_bringup, 'gui', 'gui.config']), '--force-version', '6'], + output='screen', + additional_env=env, + on_exit=Shutdown(), + sigterm_timeout='5', + sigkill_timeout='10', + log_cmd=True, + emulate_tty=True + ) + + # Clock bridge + clock_bridge = Node(package='ros_gz_bridge', executable='parameter_bridge', + name='clock_bridge', + output='screen', + arguments=[ + '/clock' + '@rosgraph_msgs/msg/Clock' + '[ignition.msgs.Clock' + ]) + + # Create launch description and add actions + ld = LaunchDescription(ARGUMENTS) + ld.add_action(ign_resource_path) + ld.add_action(ign_gui_plugin_path) + ld.add_action(ignition_gazebo) + ld.add_action(ignition_gazebo_gui) + ld.add_action(clock_bridge) + return ld diff --git a/simulation/gazebo/tb4_sim_scenario/launch/ignition_robot_launch.py b/simulation/gazebo/tb4_sim_scenario/launch/ignition_robot_launch.py new file mode 100644 index 00000000..c77a7240 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/launch/ignition_robot_launch.py @@ -0,0 +1,201 @@ +# Copyright (C) 2024 Intel Corporation +# Copyright 2021 Clearpath Robotics, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @author Roni Kreinin (rkreinin@clearpathrobotics.com) + + +from ament_index_python.packages import get_package_share_directory + +from irobot_create_common_bringup.namespace import GetNamespacedName +from irobot_create_common_bringup.offset import OffsetParser + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription +from launch.conditions import IfCondition +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution + +from launch_ros.actions import Node, PushRosNamespace + + +ARGUMENTS = [ + DeclareLaunchArgument('use_sim_time', default_value='true', + choices=['true', 'false'], + description='use_sim_time'), + DeclareLaunchArgument('model', default_value='standard', + choices=['standard', 'lite'], + description='Turtlebot4 Model'), + DeclareLaunchArgument('namespace', default_value='', + description='Robot namespace'), + DeclareLaunchArgument('spawn', default_value='True', + description='Whether to spawn the turlebot'), +] + +for pose_element in ['x', 'y', 'z', 'yaw']: + ARGUMENTS.append(DeclareLaunchArgument(pose_element, default_value='0.0', + description=f'{pose_element} component of the robot pose.')) + + +def generate_launch_description(): + + # Directories + pkg_tb4_sim_scenario = get_package_share_directory( + 'tb4_sim_scenario') + pkg_turtlebot4_ignition_bringup = get_package_share_directory( + 'turtlebot4_ignition_bringup') + pkg_irobot_create_common_bringup = get_package_share_directory( + 'irobot_create_common_bringup') + pkg_irobot_create_ignition_bringup = get_package_share_directory( + 'irobot_create_ignition_bringup') + + # Paths + # turtlebot4_ros_ign_bridge_launch = PathJoinSubstitution( + # [pkg_turtlebot4_ignition_bringup, 'launch', 'ros_ign_bridge.launch.py']) + turtlebot4_ros_ign_bridge_launch = PathJoinSubstitution( + [pkg_tb4_sim_scenario, 'launch', 'ros_ign_bridge.launch.py']) + + turtlebot4_node_launch = PathJoinSubstitution( + [pkg_turtlebot4_ignition_bringup, 'launch', 'turtlebot4_nodes.launch.py']) + + create3_nodes_launch = PathJoinSubstitution( + [pkg_irobot_create_common_bringup, 'launch', 'create3_nodes.launch.py']) + + create3_ignition_nodes_launch = PathJoinSubstitution( + [pkg_irobot_create_ignition_bringup, 'launch', 'create3_ignition_nodes.launch.py']) + + robot_description_launch = PathJoinSubstitution( + [pkg_tb4_sim_scenario, 'launch', 'robot_description.launch.py']) + + # Parameters + param_file_cmd = DeclareLaunchArgument( + 'param_file', + default_value=PathJoinSubstitution( + [pkg_turtlebot4_ignition_bringup, 'config', 'turtlebot4_node.yaml']), + description='Turtlebot4 Robot param file') + + # Launch configurations + namespace = LaunchConfiguration('namespace') + use_sim_time = LaunchConfiguration('use_sim_time') + model = LaunchConfiguration('model') + spawn = LaunchConfiguration('spawn') + x, y, z = LaunchConfiguration('x'), LaunchConfiguration('y'), LaunchConfiguration('z') + yaw = LaunchConfiguration('yaw') + turtlebot4_node_yaml_file = LaunchConfiguration('param_file') + + robot_name = GetNamespacedName(namespace, 'turtlebot4') + dock_name = GetNamespacedName(namespace, 'standard_dock') + + # Spawn robot slightly clsoer to the floor to reduce the drop + # Ensures robot remains properly docked after the drop + z_robot = OffsetParser(z, -0.0025) + + spawn_robot_group_action = GroupAction([ + PushRosNamespace(namespace), + + # Robot description + IncludeLaunchDescription( + PythonLaunchDescriptionSource([robot_description_launch]), + launch_arguments=[('model', model), + ('use_sim_time', use_sim_time)] + ), + + # Spawn TurtleBot 4 + Node( + package='ros_ign_gazebo', + condition=IfCondition(spawn), + executable='create', + arguments=['-name', robot_name, + '-x', x, + '-y', y, + '-z', z_robot, + '-Y', yaw, + '-topic', 'robot_description'], + output='screen' + ), + + # ROS IGN bridge + IncludeLaunchDescription( + PythonLaunchDescriptionSource([turtlebot4_ros_ign_bridge_launch]), + launch_arguments=[ + ('model', model), + ('robot_name', robot_name), + ('dock_name', dock_name), + ('namespace', namespace)] + ), + + # TurtleBot 4 nodes + IncludeLaunchDescription( + PythonLaunchDescriptionSource([turtlebot4_node_launch]), + launch_arguments=[('model', model), + ('param_file', turtlebot4_node_yaml_file)] + ), + + # Create 3 nodes + IncludeLaunchDescription( + PythonLaunchDescriptionSource([create3_nodes_launch]), + launch_arguments=[ + ('namespace', namespace) + ] + ), + + # Create 3 Ignition nodes + IncludeLaunchDescription( + PythonLaunchDescriptionSource([create3_ignition_nodes_launch]), + launch_arguments=[ + ('robot_name', robot_name), + ('dock_name', dock_name), + ] + ), + + # RPLIDAR static transforms + Node( + name='rplidar_stf', + package='tf2_ros', + executable='static_transform_publisher', + output='screen', + arguments=[ + '0', '0', '0', '0', '0', '0.0', + 'rplidar_link', [robot_name, '/rplidar_link/rplidar']], + remappings=[ + ('/tf', 'tf'), + ('/tf_static', 'tf_static'), + ] + ), + + # OAKD static transform + # Required for pointcloud. See https://github.com/gazebosim/gz-sensors/issues/239 + Node( + name='camera_stf', + package='tf2_ros', + executable='static_transform_publisher', + output='screen', + arguments=[ + '0', '0', '0', + '1.5707', '-1.5707', '0', + 'oakd_rgb_camera_optical_frame', + [robot_name, '/oakd_rgb_camera_frame/rgbd_camera'] + ], + remappings=[ + ('/tf', 'tf'), + ('/tf_static', 'tf_static'), + ] + ), + ]) + + # Define LaunchDescription variable + ld = LaunchDescription(ARGUMENTS) + ld.add_action(param_file_cmd) + ld.add_action(spawn_robot_group_action) + return ld diff --git a/simulation/gazebo/tb4_sim_scenario/launch/nav2_launch.py b/simulation/gazebo/tb4_sim_scenario/launch/nav2_launch.py new file mode 100644 index 00000000..a4f35c41 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/launch/nav2_launch.py @@ -0,0 +1,76 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution + + +ARGUMENTS = [ + + DeclareLaunchArgument('namespace', default_value='', + description='Robot namespace'), + # Ignition setup + DeclareLaunchArgument('world', default_value='warehouse', + description='Ignition World'), + + DeclareLaunchArgument('model', default_value='standard', + choices=['standard', 'lite'], + description='Turtlebot4 Model'), + DeclareLaunchArgument('launch_simulation', default_value='True', + description='Set "false" to skip simulation startup.'), + DeclareLaunchArgument('headless', default_value='True', + description='Start Igniton GUI or not'), + + DeclareLaunchArgument('map_yaml', default_value=[LaunchConfiguration('world'), '.yaml'], + description='map yaml file'), +] + +# Inital robot pose in the world +for pose_element in ['x', 'y', 'z', 'yaw']: + ARGUMENTS.append(DeclareLaunchArgument(pose_element, + default_value='0.0', + description=f'{pose_element} component of the robot pose.')) + + +def generate_launch_description(): + + # Directories + pkg_tb4_sim_scenario = get_package_share_directory('tb4_sim_scenario') + pkg_tb4_nav = get_package_share_directory('turtlebot4_navigation') + pkg_nav2_bringup = get_package_share_directory('nav2_bringup') + + # Launch args + map_yaml = LaunchConfiguration('map_yaml') + + nav2_bringup = IncludeLaunchDescription( + PythonLaunchDescriptionSource([PathJoinSubstitution([pkg_nav2_bringup, 'launch', 'bringup_launch.py'])]), + launch_arguments={ + 'map': PathJoinSubstitution([pkg_tb4_nav, 'maps', map_yaml]), + 'use_sim_time': 'True', + 'use_composition': 'False', + 'params_file': PathJoinSubstitution([pkg_tb4_sim_scenario, 'params', 'nav2_params.yaml']), + 'autostart': 'True'}.items() + ) + + # Create launch description + ld = LaunchDescription(ARGUMENTS) + ld.add_action(nav2_bringup) + return ld diff --git a/simulation/gazebo/tb4_sim_scenario/launch/robot_description.launch.py b/simulation/gazebo/tb4_sim_scenario/launch/robot_description.launch.py new file mode 100644 index 00000000..e5dd9ba0 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/launch/robot_description.launch.py @@ -0,0 +1,85 @@ +# Copyright (C) 2024 Intel Corporation +# Copyright 2021 Clearpath Robotics, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @author Roni Kreinin (rkreinin@clearpathrobotics.com) + + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import Command, PathJoinSubstitution +from launch.substitutions.launch_configuration import LaunchConfiguration + +from launch_ros.actions import Node + + +ARGUMENTS = [ + DeclareLaunchArgument('model', default_value='standard', + choices=['standard', 'lite'], + description='Turtlebot4 Model'), + DeclareLaunchArgument('use_sim_time', default_value='false', + choices=['true', 'false'], + description='use_sim_time'), + DeclareLaunchArgument('robot_name', default_value='turtlebot4', + description='Robot name'), + DeclareLaunchArgument('namespace', default_value=LaunchConfiguration('robot_name'), + description='Robot namespace'), +] + + +def generate_launch_description(): + pkg_tb4_sim_scenario = get_package_share_directory('tb4_sim_scenario') + xacro_file = PathJoinSubstitution([pkg_tb4_sim_scenario, + 'urdf', + 'turtlebot4.urdf.xacro']) + namespace = LaunchConfiguration('namespace') + + robot_state_publisher = Node( + package='robot_state_publisher', + executable='robot_state_publisher', + name='robot_state_publisher', + output='screen', + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + {'robot_description': Command([ + 'xacro', ' ', xacro_file, ' ', + 'gazebo:=ignition', ' ', + 'namespace:=', namespace])}, + ], + remappings=[ + ('/tf', 'tf'), + ('/tf_static', 'tf_static') + ] + ) + + joint_state_publisher = Node( + package='joint_state_publisher', + executable='joint_state_publisher', + name='joint_state_publisher', + output='screen', + parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}], + remappings=[ + ('/tf', 'tf'), + ('/tf_static', 'tf_static') + ] + ) + + # Define LaunchDescription variable + ld = LaunchDescription(ARGUMENTS) + # Add nodes to LaunchDescription + ld.add_action(robot_state_publisher) + ld.add_action(joint_state_publisher) + return ld diff --git a/simulation/gazebo/tb4_sim_scenario/launch/ros_ign_bridge.launch.py b/simulation/gazebo/tb4_sim_scenario/launch/ros_ign_bridge.launch.py new file mode 100644 index 00000000..32b3f7d6 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/launch/ros_ign_bridge.launch.py @@ -0,0 +1,221 @@ +# Copyright (C) 2024 Intel Corporation +# Copyright 2023 Clearpath Robotics, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @author Roni Kreinin (rkreinin@clearpathrobotics.com) + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription +from launch.conditions import LaunchConfigurationEquals +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration +from launch.substitutions.path_join_substitution import PathJoinSubstitution + +from launch_ros.actions import Node + +ARGUMENTS = [ + DeclareLaunchArgument('use_sim_time', default_value='true', + choices=['true', 'false'], + description='Use sim time'), + DeclareLaunchArgument('robot_name', default_value='turtlebot4', + description='Ignition model name'), + DeclareLaunchArgument('dock_name', default_value='standard_dock', + description='Ignition model name'), + DeclareLaunchArgument('namespace', default_value='', + description='Robot namespace'), + DeclareLaunchArgument('world', default_value='warehouse', + description='World name'), + DeclareLaunchArgument('scan_topic', default_value='scan', + description='Name of the scan topic'), + DeclareLaunchArgument('model', default_value='standard', + choices=['standard', 'lite'], + description='Turtlebot4 Model'), +] + + +def generate_launch_description(): + use_sim_time = LaunchConfiguration('use_sim_time') + robot_name = LaunchConfiguration('robot_name') + dock_name = LaunchConfiguration('dock_name') + namespace = LaunchConfiguration('namespace') + world = LaunchConfiguration('world') + scan_topic = LaunchConfiguration('scan_topic') + + leds = [ + 'power', + 'motors', + 'comms', + 'wifi', + 'battery', + 'user1', + 'user2' + ] + + pkg_irobot_create_ignition_bringup = get_package_share_directory( + 'irobot_create_ignition_bringup') + + create3_ros_gz_bridge_launch = PathJoinSubstitution( + [pkg_irobot_create_ignition_bringup, 'launch', 'create3_ros_ignition_bridge.launch.py']) + + create3_bridge = IncludeLaunchDescription( + PythonLaunchDescriptionSource([create3_ros_gz_bridge_launch]), + launch_arguments=[ + ('robot_name', robot_name), + ('dock_name', dock_name), + ('namespace', namespace), + ('world', world) + ] + ) + + # lidar bridge + lidar_bridge = Node( + package='ros_gz_bridge', + executable='parameter_bridge', + name='lidar_bridge', + output='screen', + parameters=[{ + 'use_sim_time': use_sim_time + }], + arguments=[ + ['/world/', world, + '/model/', robot_name, + '/link/rplidar_link/sensor/rplidar/scan' + + '@sensor_msgs/msg/LaserScan[ignition.msgs.LaserScan'] + ], + remappings=[ + (['/world/', world, + '/model/', robot_name, + '/link/rplidar_link/sensor/rplidar/scan'], + scan_topic) + ]) + + # Display message bridge + hmi_display_msg_bridge = Node( + package='ros_gz_bridge', + executable='parameter_bridge', + name='hmi_display_msg_bridge', + output='screen', + parameters=[{'use_sim_time': use_sim_time}], + arguments=[ + [namespace, '/hmi/display/raw' + + '@std_msgs/msg/String' + + ']ignition.msgs.StringMsg'], + [namespace, '/hmi/display/selected' + + '@std_msgs/msg/Int32' + + ']ignition.msgs.Int32'] + ], + remappings=[ + ([namespace, '/hmi/display/raw'], + 'hmi/display/_raw'), + ([namespace, '/hmi/display/selected'], + 'hmi/display/_selected') + ], + condition=LaunchConfigurationEquals('model', 'standard')) + + # Buttons message bridge + hmi_buttons_msg_bridge = Node( + package='ros_gz_bridge', + executable='parameter_bridge', + name='hmi_buttons_msg_bridge', + output='screen', + parameters=[{'use_sim_time': use_sim_time}], + arguments=[ + [namespace, '/hmi/buttons' + + '@std_msgs/msg/Int32' + + '[ignition.msgs.Int32'] + ], + remappings=[ + ([namespace, '/hmi/buttons'], + 'hmi/buttons/_set') + ], + condition=LaunchConfigurationEquals('model', 'standard')) + + # Buttons message bridge + hmi_led_msg_bridge = Node( + package='ros_gz_bridge', + executable='parameter_bridge', + name='hmi_led_msg_bridge', + output='screen', + parameters=[{'use_sim_time': use_sim_time}], + arguments=[ + [namespace, '/hmi/led/' + led + + '@std_msgs/msg/Int32' + + ']ignition.msgs.Int32'] for led in leds + ], + remappings=[ + ([namespace, '/hmi/led/' + led], + 'hmi/led/_' + led) for led in leds + ], + condition=LaunchConfigurationEquals('model', 'standard')) + + # Camera sensor bridge + oakd_camera_bridge = Node( + package='ros_gz_bridge', + executable='parameter_bridge', + name='camera_bridge', + output='screen', + parameters=[{'use_sim_time': use_sim_time}], + arguments=[ + ['/world/', world, + '/model/', robot_name, + '/link/oakd_rgb_camera_frame/sensor/rgbd_camera/image' + + '@sensor_msgs/msg/Image' + + '[ignition.msgs.Image'], + ['/world/', world, + '/model/', robot_name, + '/link/oakd_rgb_camera_frame/sensor/rgbd_camera/depth_image' + + '@sensor_msgs/msg/Image' + + '[ignition.msgs.Image'], + ['/world/', world, + '/model/', robot_name, + '/link/oakd_rgb_camera_frame/sensor/rgbd_camera/points' + + '@sensor_msgs/msg/PointCloud2' + + '[ignition.msgs.PointCloudPacked'], + ['/world/', world, + '/model/', robot_name, + '/link/oakd_rgb_camera_frame/sensor/rgbd_camera/camera_info' + + '@sensor_msgs/msg/CameraInfo' + + '[ignition.msgs.CameraInfo'], + ], + remappings=[ + (['/world/', world, + '/model/', robot_name, + '/link/oakd_rgb_camera_frame/sensor/rgbd_camera/image'], + 'oakd/rgb/preview/image_raw'), + (['/world/', world, + '/model/', robot_name, + '/link/oakd_rgb_camera_frame/sensor/rgbd_camera/depth_image'], + 'oakd/rgb/preview/depth'), + (['/world/', world, + '/model/', robot_name, + '/link/oakd_rgb_camera_frame/sensor/rgbd_camera/points'], + 'oakd/rgb/preview/depth/points'), + (['/world/', world, + '/model/', robot_name, + '/link/oakd_rgb_camera_frame/sensor/rgbd_camera/camera_info'], + 'oakd/rgb/preview/camera_info') + ] + ) + + # Define LaunchDescription variable + ld = LaunchDescription(ARGUMENTS) + ld.add_action(create3_bridge) + ld.add_action(hmi_display_msg_bridge) + ld.add_action(hmi_buttons_msg_bridge) + ld.add_action(hmi_led_msg_bridge) + ld.add_action(lidar_bridge) + ld.add_action(oakd_camera_bridge) + return ld diff --git a/simulation/gazebo/tb4_sim_scenario/launch/sim_nav_scenario_launch.py b/simulation/gazebo/tb4_sim_scenario/launch/sim_nav_scenario_launch.py new file mode 100644 index 00000000..c89f12ee --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/launch/sim_nav_scenario_launch.py @@ -0,0 +1,108 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os + +from ament_index_python.packages import get_package_share_directory + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, SetLaunchConfiguration +from launch.conditions import IfCondition, UnlessCondition +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution + + +def generate_launch_description(): + + tb4_sim_scenario_dir = get_package_share_directory('tb4_sim_scenario') + scenario_execution_dir = get_package_share_directory('scenario_execution') + message_modification_dir = get_package_share_directory('message_modification') + tf_to_pose_publisher_dir = get_package_share_directory('tf_to_pose_publisher') + + scenario = LaunchConfiguration('scenario') + scenario_execution = LaunchConfiguration('scenario_execution') + arg_scenario = DeclareLaunchArgument('scenario', + description='Scenario file to execute') + arg_scenario_execution = DeclareLaunchArgument( + 'scenario_execution', default_value='True', + description='Wether to execute scenario execution') + world = LaunchConfiguration('world') + arg_world = DeclareLaunchArgument('world', default_value='maze', + description='Ignition World') + + faulty_scan = LaunchConfiguration('faulty_scan') + arg_faulty_scan = DeclareLaunchArgument( + 'faulty_scan', + default_value='false', + choices=['false', 'true'], + description='Whether to inject sensor faults to scan message') + + ignition = IncludeLaunchDescription( + PythonLaunchDescriptionSource([PathJoinSubstitution([tb4_sim_scenario_dir, 'launch', 'ignition_launch.py'])]), + ) + + robot_spawn = IncludeLaunchDescription( + PythonLaunchDescriptionSource([PathJoinSubstitution([tb4_sim_scenario_dir, 'launch', 'ignition_robot_launch.py'])]), + launch_arguments=[ + ('scan_topic', LaunchConfiguration('sim_scan_topic')), + ] + ) + + nav2_bringup = IncludeLaunchDescription( + PythonLaunchDescriptionSource([PathJoinSubstitution([tb4_sim_scenario_dir, 'launch', 'nav2_launch.py'])]), + ) + + scan_modification = IncludeLaunchDescription( + PythonLaunchDescriptionSource(os.path.join(message_modification_dir, 'launch', 'scan_modification_launch.py')), + condition=IfCondition(faulty_scan), + launch_arguments=[ + ('ign_pose_topic', ['/world/', world, '/dynamic_pose/info']), + ] + ) + + scenario_exec = IncludeLaunchDescription( + PythonLaunchDescriptionSource([PathJoinSubstitution([scenario_execution_dir, 'launch', 'scenario_launch.py'])]), + condition=IfCondition(scenario_execution), + launch_arguments=[ + ('scenario', scenario), + ] + ) + + tf_to_pose = IncludeLaunchDescription( + PythonLaunchDescriptionSource([PathJoinSubstitution([tf_to_pose_publisher_dir, 'launch', 'tf_to_pose_launch.py'])]), + ) + + ld = LaunchDescription([ + arg_scenario, + arg_scenario_execution, + arg_world, + arg_faulty_scan, + SetLaunchConfiguration( + name='sim_scan_topic', + condition=IfCondition(faulty_scan), + value='scan_sim'), + SetLaunchConfiguration( + name='sim_scan_topic', + condition=UnlessCondition(faulty_scan), + value='scan'), + ]) + ld.add_action(ignition) + ld.add_action(robot_spawn) + ld.add_action(nav2_bringup) + ld.add_action(scan_modification) + ld.add_action(scenario_exec) + ld.add_action(tf_to_pose) + return ld diff --git a/simulation/gazebo/tb4_sim_scenario/package.xml b/simulation/gazebo/tb4_sim_scenario/package.xml new file mode 100644 index 00000000..383916da --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/package.xml @@ -0,0 +1,27 @@ + + + + tb4_sim_scenario + 1.0.0 + TurtleBot 4 Simulation Scenario Execution + Intel Labs + Intel Labs + Apache-2.0 + + ament_cmake + + + turtlebot4_simulator + turtlebot4_description + scenario_execution + message_modification + tf_to_pose_publisher + + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/simulation/gazebo/tb4_sim_scenario/params/nav2_params.yaml b/simulation/gazebo/tb4_sim_scenario/params/nav2_params.yaml new file mode 100644 index 00000000..2f997af4 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/params/nav2_params.yaml @@ -0,0 +1,440 @@ +--- +amcl: + ros__parameters: + use_sim_time: True + alpha1: 0.2 + alpha2: 0.2 + alpha3: 0.2 + alpha4: 0.2 + alpha5: 0.2 + base_frame_id: "base_link" + beam_skip_distance: 0.5 + beam_skip_error_threshold: 0.9 + beam_skip_threshold: 0.3 + do_beamskip: false + global_frame_id: "map" + lambda_short: 0.1 + laser_likelihood_max_dist: 2.0 + laser_max_range: 100.0 + laser_min_range: -1.0 + laser_model_type: "likelihood_field" + max_beams: 60 + max_particles: 2000 + min_particles: 500 + odom_frame_id: "odom" + pf_err: 0.05 + pf_z: 0.99 + recovery_alpha_fast: 0.0 + recovery_alpha_slow: 0.0 + resample_interval: 1 + robot_model_type: "nav2_amcl::DifferentialMotionModel" + save_pose_rate: 0.5 + sigma_hit: 0.2 + tf_broadcast: true + transform_tolerance: 1.0 + update_min_a: 0.2 + update_min_d: 0.25 + z_hit: 0.5 + z_max: 0.05 + z_rand: 0.5 + z_short: 0.05 + scan_topic: scan + +bt_navigator: + ros__parameters: + use_sim_time: True + global_frame: map + robot_base_frame: base_link + odom_topic: /odom + bt_loop_duration: 10 + default_server_timeout: 20 + # 'default_nav_through_poses_bt_xml' and 'default_nav_to_pose_bt_xml' + # are use defaults: + # nav2_bt_navigator/navigate_to_pose_w_replanning_and_recovery.xml + # nav2_bt_navigator/navigate_through_poses_w_replanning_and_recovery.xml + # They can be set here or via a RewrittenYaml remap from a parent launch + # ile to Nav2. + plugin_lib_names: + - nav2_compute_path_to_pose_action_bt_node + - nav2_compute_path_through_poses_action_bt_node + - nav2_smooth_path_action_bt_node + - nav2_follow_path_action_bt_node + - nav2_spin_action_bt_node + - nav2_wait_action_bt_node + - nav2_assisted_teleop_action_bt_node + - nav2_back_up_action_bt_node + - nav2_drive_on_heading_bt_node + - nav2_clear_costmap_service_bt_node + - nav2_is_stuck_condition_bt_node + - nav2_goal_reached_condition_bt_node + - nav2_goal_updated_condition_bt_node + - nav2_globally_updated_goal_condition_bt_node + - nav2_is_path_valid_condition_bt_node + - nav2_initial_pose_received_condition_bt_node + - nav2_reinitialize_global_localization_service_bt_node + - nav2_rate_controller_bt_node + - nav2_distance_controller_bt_node + - nav2_speed_controller_bt_node + - nav2_truncate_path_action_bt_node + - nav2_truncate_path_local_action_bt_node + - nav2_goal_updater_node_bt_node + - nav2_recovery_node_bt_node + - nav2_pipeline_sequence_bt_node + - nav2_round_robin_node_bt_node + - nav2_transform_available_condition_bt_node + - nav2_time_expired_condition_bt_node + - nav2_path_expiring_timer_condition + - nav2_distance_traveled_condition_bt_node + - nav2_single_trigger_bt_node + - nav2_goal_updated_controller_bt_node + - nav2_is_battery_low_condition_bt_node + - nav2_navigate_through_poses_action_bt_node + - nav2_navigate_to_pose_action_bt_node + - nav2_remove_passed_goals_action_bt_node + - nav2_planner_selector_bt_node + - nav2_controller_selector_bt_node + - nav2_goal_checker_selector_bt_node + - nav2_controller_cancel_bt_node + - nav2_path_longer_on_approach_bt_node + - nav2_wait_cancel_bt_node + - nav2_spin_cancel_bt_node + - nav2_back_up_cancel_bt_node + - nav2_assisted_teleop_cancel_bt_node + - nav2_drive_on_heading_cancel_bt_node + - nav2_is_battery_charging_condition_bt_node + +bt_navigator_navigate_through_poses_rclcpp_node: + ros__parameters: + use_sim_time: True + +bt_navigator_navigate_to_pose_rclcpp_node: + ros__parameters: + use_sim_time: True + +controller_server: + ros__parameters: + use_sim_time: True + controller_frequency: 20.0 + min_x_velocity_threshold: 0.001 + min_y_velocity_threshold: 0.5 + min_theta_velocity_threshold: 0.001 + failure_tolerance: 0.3 + progress_checker_plugin: "progress_checker" + goal_checker_plugins: ["general_goal_checker"] # "precise_goal_checker" + controller_plugins: ["FollowPath"] + + # Progress checker parameters + progress_checker: + plugin: "nav2_controller::SimpleProgressChecker" + required_movement_radius: 0.5 + movement_time_allowance: 10.0 + # Goal checker parameters + #precise_goal_checker: + # plugin: "nav2_controller::SimpleGoalChecker" + # xy_goal_tolerance: 0.25 + # yaw_goal_tolerance: 0.25 + # stateful: True + general_goal_checker: + stateful: True + plugin: "nav2_controller::SimpleGoalChecker" + xy_goal_tolerance: 0.25 + yaw_goal_tolerance: 0.25 + # DWB parameters + FollowPath: + plugin: "dwb_core::DWBLocalPlanner" + debug_trajectory_details: True + min_vel_x: 0.0 + min_vel_y: 0.0 + max_vel_x: 0.26 + max_vel_y: 0.0 + max_vel_theta: 1.0 + min_speed_xy: 0.0 + max_speed_xy: 0.26 + min_speed_theta: 0.0 + # Add high threshold velocity for turtlebot 3 issue. + # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75 + acc_lim_x: 2.5 + acc_lim_y: 0.0 + acc_lim_theta: 3.2 + decel_lim_x: -2.5 + decel_lim_y: 0.0 + decel_lim_theta: -3.2 + vx_samples: 20 + vy_samples: 5 + vtheta_samples: 20 + sim_time: 1.7 + linear_granularity: 0.05 + angular_granularity: 0.025 + transform_tolerance: 0.2 + xy_goal_tolerance: 0.25 + trans_stopped_velocity: 0.25 + short_circuit_trajectory_evaluation: True + stateful: True + critics: + - "RotateToGoal" + - "Oscillation" + - "BaseObstacle" + - "GoalAlign" + - "PathAlign" + - "PathDist" + - "GoalDist" + BaseObstacle.scale: 0.02 + PathAlign.scale: 32.0 + PathAlign.forward_point_distance: 0.1 + GoalAlign.scale: 24.0 + GoalAlign.forward_point_distance: 0.1 + PathDist.scale: 32.0 + GoalDist.scale: 24.0 + RotateToGoal.scale: 32.0 + RotateToGoal.slowing_factor: 5.0 + RotateToGoal.lookahead_time: -1.0 + +local_costmap: + local_costmap: + ros__parameters: + update_frequency: 5.0 + publish_frequency: 2.0 + global_frame: odom + robot_base_frame: base_link + use_sim_time: True + rolling_window: true + width: 3 + height: 3 + resolution: 0.05 + robot_radius: 0.22 + plugins: ["voxel_layer", "inflation_layer"] + inflation_layer: + plugin: "nav2_costmap_2d::InflationLayer" + cost_scaling_factor: 3.0 + inflation_radius: 0.55 + voxel_layer: + plugin: "nav2_costmap_2d::VoxelLayer" + enabled: True + publish_voxel_map: True + origin_z: 0.0 + z_resolution: 0.05 + z_voxels: 16 + max_obstacle_height: 2.0 + mark_threshold: 0 + observation_sources: scan + scan: + topic: /scan + max_obstacle_height: 2.0 + clearing: True + marking: True + data_type: "LaserScan" + raytrace_max_range: 3.0 + raytrace_min_range: 0.0 + obstacle_max_range: 2.5 + obstacle_min_range: 0.0 + static_layer: + plugin: "nav2_costmap_2d::StaticLayer" + map_subscribe_transient_local: True + always_send_full_costmap: True + +global_costmap: + global_costmap: + ros__parameters: + update_frequency: 1.0 + publish_frequency: 1.0 + global_frame: map + robot_base_frame: base_link + use_sim_time: True + robot_radius: 0.22 + resolution: 0.05 + track_unknown_space: true + plugins: ["static_layer", "obstacle_layer", "inflation_layer"] + obstacle_layer: + plugin: "nav2_costmap_2d::ObstacleLayer" + enabled: True + observation_sources: scan + scan: + topic: /scan + max_obstacle_height: 2.0 + clearing: True + marking: True + data_type: "LaserScan" + raytrace_max_range: 3.0 + raytrace_min_range: 0.0 + obstacle_max_range: 2.5 + obstacle_min_range: 0.0 + static_layer: + plugin: "nav2_costmap_2d::StaticLayer" + map_subscribe_transient_local: True + inflation_layer: + plugin: "nav2_costmap_2d::InflationLayer" + cost_scaling_factor: 3.0 + inflation_radius: 0.55 + always_send_full_costmap: True + +map_server: + ros__parameters: + use_sim_time: True + # Overridden in launch by the "map" launch configuration or provided + # default value. To use in yaml, remove the default "map" value in the + # tb3_simulation_launch.py file & provide full path to map below. + yaml_filename: "" + +map_saver: + ros__parameters: + use_sim_time: True + save_map_timeout: 5.0 + free_thresh_default: 0.25 + occupied_thresh_default: 0.65 + map_subscribe_transient_local: True + +planner_server: + ros__parameters: + expected_planner_frequency: 20.0 + use_sim_time: True + planner_plugins: ["GridBased"] + GridBased: + plugin: "nav2_navfn_planner/NavfnPlanner" + tolerance: 0.5 + use_astar: false + allow_unknown: true + +smoother_server: + ros__parameters: + use_sim_time: True + smoother_plugins: ["simple_smoother"] + simple_smoother: + plugin: "nav2_smoother::SimpleSmoother" + tolerance: 1.0e-10 + max_its: 1000 + do_refinement: True + +behavior_server: + ros__parameters: + costmap_topic: local_costmap/costmap_raw + footprint_topic: local_costmap/published_footprint + cycle_frequency: 10.0 + behavior_plugins: + - "spin" + - "backup" + - "drive_on_heading" + - "assisted_teleop" + - "wait" + spin: + plugin: "nav2_behaviors/Spin" + backup: + plugin: "nav2_behaviors/BackUp" + drive_on_heading: + plugin: "nav2_behaviors/DriveOnHeading" + wait: + plugin: "nav2_behaviors/Wait" + assisted_teleop: + plugin: "nav2_behaviors/AssistedTeleop" + global_frame: odom + robot_base_frame: base_link + transform_tolerance: 0.1 + use_sim_time: true + simulate_ahead_time: 2.0 + max_rotational_vel: 1.0 + min_rotational_vel: 0.4 + rotational_acc_lim: 3.2 + +robot_state_publisher: + ros__parameters: + use_sim_time: True + +waypoint_follower: + ros__parameters: + use_sim_time: True + loop_rate: 20 + stop_on_failure: false + waypoint_task_executor_plugin: "wait_at_waypoint" + wait_at_waypoint: + plugin: "nav2_waypoint_follower::WaitAtWaypoint" + enabled: True + waypoint_pause_duration: 200 + +velocity_smoother: + ros__parameters: + use_sim_time: True + smoothing_frequency: 20.0 + scale_velocities: False + feedback: "OPEN_LOOP" + max_velocity: [0.26, 0.0, 1.0] + min_velocity: [-0.26, 0.0, -1.0] + max_accel: [2.5, 0.0, 3.2] + max_decel: [-2.5, 0.0, -3.2] + odom_topic: "odom" + odom_duration: 0.1 + deadband_velocity: [0.0, 0.0, 0.0] + velocity_timeout: 1.0 + +slam_toolbox: + ros__parameters: + + # Plugin params + solver_plugin: solver_plugins::CeresSolver + ceres_linear_solver: SPARSE_NORMAL_CHOLESKY + ceres_preconditioner: SCHUR_JACOBI + ceres_trust_strategy: LEVENBERG_MARQUARDT + ceres_dogleg_type: TRADITIONAL_DOGLEG + ceres_loss_function: None + + # ROS Parameters + odom_frame: odom + map_frame: map + base_frame: base_link + scan_topic: /scan + mode: mapping #localization + + # if you'd like to immediately start continuing a map at a given pose + # or at the dock, but they are mutually exclusive, if pose is given + # will use pose + #map_file_name: test_steve + #map_start_pose: [0.0, 0.0, 0.0] + #map_start_at_dock: true + + debug_logging: false + throttle_scans: 1 + transform_publish_period: 0.02 #if 0 never publishes odometry + map_update_interval: 5.0 + resolution: 0.05 + max_laser_range: 20.0 #for rastering images + minimum_time_interval: 0.5 + transform_timeout: 0.7 + tf_buffer_duration: 30. + stack_size_to_use: 40000000 # program needs a larger stack size + # to serialize large maps + enable_interactive_mode: true + + # General Parameters + use_scan_matching: true + use_scan_barycenter: true + minimum_travel_distance: 0.5 + minimum_travel_heading: 0.5 + scan_buffer_size: 10 + scan_buffer_maximum_scan_distance: 10.0 + link_match_minimum_response_fine: 0.1 + link_scan_maximum_distance: 1.5 + loop_search_maximum_distance: 3.0 + do_loop_closing: true + loop_match_minimum_chain_size: 10 + loop_match_maximum_variance_coarse: 3.0 + loop_match_minimum_response_coarse: 0.35 + loop_match_minimum_response_fine: 0.45 + + # Correlation Parameters - Correlation Parameters + correlation_search_space_dimension: 0.5 + correlation_search_space_resolution: 0.01 + correlation_search_space_smear_deviation: 0.1 + + # Correlation Parameters - Loop Closure Parameters + loop_search_space_dimension: 8.0 + loop_search_space_resolution: 0.05 + loop_search_space_smear_deviation: 0.03 + + # Scan Matcher Parameters + distance_variance_penalty: 0.5 + angle_variance_penalty: 1.0 + + fine_search_angle_offset: 0.00349 + coarse_search_angle_offset: 0.349 + coarse_angle_resolution: 0.0349 + minimum_angle_penalty: 0.9 + minimum_distance_penalty: 0.5 + use_response_expansion: true diff --git a/simulation/gazebo/tb4_sim_scenario/urdf/create3.urdf.xacro b/simulation/gazebo/tb4_sim_scenario/urdf/create3.urdf.xacro new file mode 100644 index 00000000..34dd4cc6 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/urdf/create3.urdf.xacro @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(find irobot_create_control)/config/control.yaml + + ~/odom:=odom + /tf:=tf + /tf_static:=tf_static + /diagnostics:=diagnostics + $(arg namespace) + + + + + + + + + $(find irobot_create_control)/config/control.yaml + + ~/odom:=odom + /tf:=tf + /tf_static:=tf_static + /diagnostics:=diagnostics + $(arg namespace) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(arg namespace) + odom:=sim_ground_truth_pose + + base_link + world + 62 + 0 0 0 + 0.0 0.0 0.0 + 0.0 + + + + + + + + $(arg namespace) + ~/out:=dock_status + + 1.0 + ${robot_model_name} + ${receiver_link_name} + ${dock_model_name} + ${emitter_link_name} + + + + + + + + true + true + true + 62 + + + + + diff --git a/simulation/gazebo/tb4_sim_scenario/urdf/turtlebot4.urdf.xacro b/simulation/gazebo/tb4_sim_scenario/urdf/turtlebot4.urdf.xacro new file mode 100644 index 00000000..e57e1948 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/urdf/turtlebot4.urdf.xacro @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/simulation/gazebo/tb4_sim_scenario/worlds/maze.sdf b/simulation/gazebo/tb4_sim_scenario/worlds/maze.sdf new file mode 100644 index 00000000..274a8620 --- /dev/null +++ b/simulation/gazebo/tb4_sim_scenario/worlds/maze.sdf @@ -0,0 +1,1066 @@ + + + + + + 0.003 + 1 + 1000 + + + + + + + ogre + + + + 1 + 0 0 10 0 -0 0 + 0.8 0.8 0.8 1 + 0.2 0.2 0.2 1 + + 1000 + 0.90000000000000002 + 0.01 + 0.001 + + -0.5 0.1 -0.9 + + 0 + 0 + 0 + + + 0 0 -9.8 + 6e-06 2.3e-05 -4.2e-05 + + + 0.4 0.4 0.4 1 + 0.7 0.7 0.7 1 + 1 + + + 1 + + + + + 0 0 1 + 100 100 + + + + + + + + + + + + + + 0 0 1 + 100 100 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + 0 0 0 0 -0 0 + + + + 0 0 1 0 0 0 + true + + -10.005 0 0 0 0 0 + true + + + + 0.01 20 2 + + + + + + + 0.01 20 2 + + + + 0.1 0.1 0.1 1 + 0 0.01 0.05 1 + 0 0.01 0.05 1 + + + + + 10.005 0 0 0 0 0 + true + + + + 0.01 20 2 + + + + + + + 0.01 20 2 + + + + 0.1 0.1 0.1 1 + 0 0.01 0.05 1 + 0 0.01 0.05 1 + + + + + 0 10.005 0 0 0 0 + true + + + + 20 0.01 2 + + + + + + + 20 0.01 2 + + + + 0.1 0.1 0.1 1 + 0 0.01 0.05 1 + 0 0.01 0.05 1 + + + + + 0 -10.005 0 0 0 0 + true + + + + 20 0.01 2 + + + + 0.1 0.1 0.1 1 + 0 0.01 0.05 1 + 0 0.01 0.05 1 + + + + + + 20 0.01 2 + + + + 0.1 0.1 0.1 1 + 0 0.01 0.05 1 + 0 0.01 0.05 1 + + + + + + + 0 0 0.5 0 0 0 + true + + -6.5 -9 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -8.5 -6.5 0 0 0 0 + true + + + + 3 1 1 + + + + + + + 3 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -3.5 -7 0 0 0 0 + true + + + + 1 6 1 + + + + + + + 1 6 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 2.5 -9 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 7 -8.5 0 0 0 0 + true + + + + 2 1 1 + + + + + + + 2 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -9 -0.5 0 0 0 0 + true + + + + 2 1 1 + + + + + + + 2 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -8.5 -2.5 0 0 0 0 + true + + + + 1 3 1 + + + + + + + 1 3 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -4.5 -1.5 0 0 0 0 + true + + + + 1 1 1 + + + + + + + 1 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -6 1 0 0 0 0 + true + + + + 2 2 1 + + + + + + + 2 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -7.5 4.5 0 0 0 0 + true + + + + 5 1 1 + + + + + + + 5 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -3 7.5 0 0 0 0 + true + + + + 4 1 1 + + + + + + + 4 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + 0 5.5 0 0 0 0 + true + + + + 2 9 1 + + + + + + + 2 9 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 0 -2.5 0 0 0 0 + true + + + + 2 1 1 + + + + + + + 2 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -0.5 -1.5 0 0 0 0 + true + + + + 1 1 1 + + + + + + + 1 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + -0.5 -4.5 0 0 0 0 + true + + + + 1 1 1 + + + + + + + 1 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 0.5 -5.5 0 0 0 0 + true + + + + 3 1 1 + + + + + + + 3 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 2.5 -4.5 0 0 0 0 + true + + + + 3 1 1 + + + + + + + 3 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 4.5 -5.5 0 0 0 0 + true + + + + 3 1 1 + + + + + + + 3 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 6.5 -4.5 0 0 0 0 + true + + + + 3 1 1 + + + + + + + 3 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 8.5 -5.5 0 0 0 0 + true + + + + 3 1 1 + + + + + + + 3 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 9.5 -4.5 0 0 0 0 + true + + + + 1 1 1 + + + + + + + 1 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 3.5 -0.5 0 0 0 0 + true + + + + 1 3 1 + + + + + + + 1 3 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 7 0.5 0 0 0 0 + true + + + + 6 1 1 + + + + + + + 6 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 7.5 2 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 1.5 5.5 0 0 0 0 + true + + + + 1 1 1 + + + + + + + 1 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 2.5 6 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 3.5 7.5 0 0 0 0 + true + + + + 1 3 1 + + + + + + + 1 3 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 4.5 8.5 0 0 0 0 + true + + + + 1 1 1 + + + + + + + 1 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 5.5 9 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 6.5 9.5 0 0 0 0 + true + + + + 1 1 1 + + + + + + + 1 1 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 4.5 3 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 5.5 4 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 6.5 5 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 7.5 6 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 8.5 7 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + 9.5 8 0 0 0 0 + true + + + + 1 2 1 + + + + + + + 1 2 1 + + + + 0.1 0.1 0.1 1 + 0 0.1 0.2 1 + 0 0.01 0.05 1 + + + + + + \ No newline at end of file diff --git a/tools/message_modification/README.md b/tools/message_modification/README.md new file mode 100644 index 00000000..3fdf79bd --- /dev/null +++ b/tools/message_modification/README.md @@ -0,0 +1,3 @@ +# Message modification + +The `message_modification` packages provides ros nodes that receive ROS messages, modify the content and republish them. \ No newline at end of file diff --git a/tools/message_modification/launch/scan_modification_launch.py b/tools/message_modification/launch/scan_modification_launch.py new file mode 100644 index 00000000..e9eaa341 --- /dev/null +++ b/tools/message_modification/launch/scan_modification_launch.py @@ -0,0 +1,68 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# The main goal of this launch file is the visualization of the tf output topic in rviz2 +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, GroupAction +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node, PushRosNamespace + +ARGUMENTS = [ + DeclareLaunchArgument('namespace', default_value='', + description='Robot namespace (without leading /)'), + + DeclareLaunchArgument('in_topic_scan', default_value='scan_sim', + description='Whether to execute simulation gui'), + + DeclareLaunchArgument('out_topic_scan', default_value='scan', + description='Whether to execute simulation gui'), + + DeclareLaunchArgument('drop_percentage', default_value='0.0', + description='Amount of dropped scan points in %'), + + DeclareLaunchArgument('std_dev_noise', default_value='0.0', + description='Whether to execute simulation gui'), +] + + +def generate_launch_description(): + namespace = LaunchConfiguration("namespace") + in_topic_scan = LaunchConfiguration("in_topic_scan") + out_topic_scan = LaunchConfiguration("out_topic_scan") + drop_percentage = LaunchConfiguration("drop_percentage") + std_dev_noise = LaunchConfiguration("std_dev_noise") + + modification_group_action = GroupAction( + [ + PushRosNamespace(namespace), + Node( + package="message_modification", + name="laserscan_modification", + executable="laserscan_modification", + remappings=[ + ("in", in_topic_scan), + ("out", out_topic_scan), + ], + parameters=[ + {"random_drop_percentage": drop_percentage}, + {"gaussian_noise_std_deviation": std_dev_noise}, + ], + ), + ] + ) + ld = LaunchDescription(ARGUMENTS) + ld.add_action(modification_group_action) + return ld diff --git a/tools/message_modification/message_modification/__init__.py b/tools/message_modification/message_modification/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/tools/message_modification/message_modification/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/tools/message_modification/message_modification/laserscan_modification.py b/tools/message_modification/message_modification/laserscan_modification.py new file mode 100755 index 00000000..1248b08e --- /dev/null +++ b/tools/message_modification/message_modification/laserscan_modification.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import rclpy +from rclpy.node import Node +from rclpy.qos import QoSPresetProfiles + +from rclpy.parameter import Parameter +from rcl_interfaces.msg import SetParametersResult + +from sensor_msgs.msg import LaserScan +import numpy as np + + +class LaserscanModification(Node): + + def __init__(self): + super().__init__('laserscan_modification') + + self.declare_parameter('random_drop_percentage', 0.0) + self.random_drop_percentage = self.get_parameter('random_drop_percentage').value + self.declare_parameter('gaussian_noise_std_deviation', 0.0) + self.gaussian_noise_std_deviation = self.get_parameter('gaussian_noise_std_deviation').value + + self.add_on_set_parameters_callback(self.parameter_callback) + + self.out_publisher = self.create_publisher(LaserScan, "out", qos_profile=QoSPresetProfiles.SENSOR_DATA.value) + self.in_subscriber = self.create_subscription(LaserScan, 'in', self.callback, qos_profile=QoSPresetProfiles.SENSOR_DATA.value) + + def parameter_callback(self, params): + result = True + for param in params: + if param.name == 'random_drop_percentage' and param.type_ == Parameter.Type.DOUBLE: + if param.value <= 1.0 and param.value >= 0.0: + self.random_drop_percentage = param.value + else: + result = False + elif param.name == 'gaussian_noise_std_deviation' and param.type_ == Parameter.Type.DOUBLE: + self.gaussian_noise_std_deviation = param.value + else: + result = False + + self.get_logger().info(f"Parameter update {params}: success={result}") + return SetParametersResult(successful=result) + + def callback(self, msg): + if self.random_drop_percentage != 0.0: + ranges = np.asarray(msg.ranges) + indices = np.random.choice(np.arange(len(msg.ranges)), replace=False, + size=int(len(msg.ranges) * self.random_drop_percentage)) + ranges[indices] = np.inf + msg.ranges = ranges.tolist() + if self.gaussian_noise_std_deviation != 0.0: + gaussian_noise = np.random.normal(0, self.gaussian_noise_std_deviation, len(msg.ranges)) + ranges_with_noise = msg.ranges + gaussian_noise + msg.ranges = ranges_with_noise.tolist() + self.out_publisher.publish(msg) + + +def main(args=None): + rclpy.init(args=args) + laserscan_modification = LaserscanModification() + + executor = rclpy.executors.MultiThreadedExecutor() + executor.add_node(laserscan_modification) + + try: + executor.spin() + except KeyboardInterrupt: + laserscan_modification.get_logger().info("User requested shut down.") + finally: + rclpy.shutdown() + + +if __name__ == "__main__": + main() diff --git a/tools/message_modification/message_modification/message_drop.py b/tools/message_modification/message_modification/message_drop.py new file mode 100755 index 00000000..e3b3053b --- /dev/null +++ b/tools/message_modification/message_modification/message_drop.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import rclpy +from rclpy.node import Node +from rclpy.qos import QoSPresetProfiles + +from rclpy.parameter import Parameter +from rcl_interfaces.msg import SetParametersResult + +import importlib +import random + + +class MessageDrop(Node): + + def __init__(self): + super().__init__('message_drop') + + self.declare_parameter('message_type', rclpy.Parameter.Type.STRING) + datatype_in_list = self.get_parameter('message_type').value.split(".") + self.topic_type = getattr(importlib.import_module(".".join(datatype_in_list[0:-1])), datatype_in_list[-1]) + + self.declare_parameter('drop_rate', 0.0) + self.drop_rate = self.get_parameter('drop_rate').value + + self.out_publisher = self.create_publisher(self.topic_type, "out", qos_profile=QoSPresetProfiles.SENSOR_DATA.value) + self.in_subscriber = self.create_subscription(self.topic_type, 'in', self.callback, qos_profile=QoSPresetProfiles.SENSOR_DATA.value) + + def parameter_callback(self, params): + result = True + for param in params: + if param.name == 'drop_rate' and param.type_ == Parameter.Type.DOUBLE: + if param.value <= 1.0: + self.drop_rate = param.value + else: + result = False + else: + result = False + self.get_logger().info(f"Parameter update {params}. result={result}") + return SetParametersResult(successful=result) + + def callback(self, msg): + drop = random.random() <= self.drop_rate # nosec B311 + if not drop: + self.out_publisher.publish(msg) + + +def main(args=None): + rclpy.init(args=args) + node = MessageDrop() + + executor = rclpy.executors.MultiThreadedExecutor() + executor.add_node(node) + + try: + executor.spin() + except KeyboardInterrupt: + node.get_logger().info("User requested shut down.") + finally: + rclpy.shutdown() + + +if __name__ == "__main__": + main() diff --git a/tools/message_modification/package.xml b/tools/message_modification/package.xml new file mode 100644 index 00000000..bc6f237f --- /dev/null +++ b/tools/message_modification/package.xml @@ -0,0 +1,17 @@ + + + message_modification + 1.0.0 + Modification of message data + Intel Labs + Intel Labs + Apache-2.0 + + sensor_msgs + + rclpy + + + ament_python + + diff --git a/tools/message_modification/resource/message_modification b/tools/message_modification/resource/message_modification new file mode 100644 index 00000000..e69de29b diff --git a/tools/message_modification/setup.cfg b/tools/message_modification/setup.cfg new file mode 100644 index 00000000..3b78448f --- /dev/null +++ b/tools/message_modification/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/message_modification +[install] +install_scripts=$base/lib/message_modification \ No newline at end of file diff --git a/tools/message_modification/setup.py b/tools/message_modification/setup.py new file mode 100644 index 00000000..90f3389e --- /dev/null +++ b/tools/message_modification/setup.py @@ -0,0 +1,47 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +Setup for message_modification +""" +import os +from glob import glob + +from setuptools import setup + +PACKAGE_NAME = 'message_modification' +setup( + name=PACKAGE_NAME, + version='1.0.0', + packages=[PACKAGE_NAME], + data_files=[ + ('share/ament_index/resource_index/packages', ['resource/' + PACKAGE_NAME]), + (os.path.join('share', PACKAGE_NAME), ['package.xml']), + (os.path.join('share', PACKAGE_NAME, 'launch'), glob('launch/*launch.py')) + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Message modification', + license='Apache License 2.0', + entry_points={ + 'console_scripts': [ + 'laserscan_modification = message_modification.laserscan_modification:main', + 'message_drop = message_modification.message_drop:main', + ], + }, +) diff --git a/tools/scenario_status/README.md b/tools/scenario_status/README.md new file mode 100644 index 00000000..ae457f2b --- /dev/null +++ b/tools/scenario_status/README.md @@ -0,0 +1,3 @@ +# Scenario Status + +The `scenario_status` packages provides the scenario_status ROS node that publishes the current scenario status to a topic. During ros-bag recording this allows to directly match data with a scenario status. \ No newline at end of file diff --git a/tools/scenario_status/package.xml b/tools/scenario_status/package.xml new file mode 100644 index 00000000..820189f2 --- /dev/null +++ b/tools/scenario_status/package.xml @@ -0,0 +1,29 @@ + + + + scenario_status + 1.0.0 + + Simple node to call a service to publish the py-trees-\ + behaviour tree to a topic, then subscribe to that topic and publish \ + changes in behaviour states as strings at the time they are \ + happening + + Intel Labs + Intel Labs + Apache-2.0 + + rclpy + py_trees_ros_interfaces + scenario_execution_interfaces + rosgraph_msgs + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/tools/scenario_status/resource/scenario_status b/tools/scenario_status/resource/scenario_status new file mode 100644 index 00000000..e69de29b diff --git a/tools/scenario_status/scenario_status/__init__.py b/tools/scenario_status/scenario_status/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/tools/scenario_status/scenario_status/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/tools/scenario_status/scenario_status/scenario_status_node.py b/tools/scenario_status/scenario_status/scenario_status_node.py new file mode 100644 index 00000000..4bc6efda --- /dev/null +++ b/tools/scenario_status/scenario_status/scenario_status_node.py @@ -0,0 +1,204 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +"""node to publish the status of the running scenario to a ROS topic""" +#!/usr/bin/env python3 + +import rclpy +from rclpy import qos +from rclpy.node import Node +from rcl_interfaces.msg import ParameterDescriptor + +from py_trees_ros_interfaces.srv import OpenSnapshotStream +from py_trees_ros_interfaces.msg import BehaviourTree +from scenario_execution_interfaces.msg import ScenarioStatus as ScenarioStatusMsg # pylint: disable=no-name-in-module +from rosgraph_msgs.msg import Clock + + +class ScenarioStatus(Node): + + """Simple node to call a service to publish the py-trees-behaviour tree + to a topic, then subscribe to that topic and publish changes in behaviour + states as strings at the time they are happening.""" + + def __init__(self): + """initialize node""" + super().__init__('scenario_status') + + self.bt_topic = None + self.snapshot_srv_name = None + self.scenario_status_topic = None + self.behaviour_infos = None + self.last_behaviour_infos = None + self.scenario_bt = None + self.time = None + # self.client_node = None + self.logger = self.get_logger() + + self.states_dict = { + 1: 'INVALID', + 2: 'RUNNING', + 3: 'SUCCESS', + 4: 'FAILURE' + } + self.types_dict = { + # Possible types of behaviour + 0: 'UNKNOWN_TYPE', + 1: 'BEHAVIOUR', + 2: 'SEQUENCE', + 3: 'SELECTOR', + 4: 'PARALLEL', + 5: 'CHOOSER', + 6: 'DECORATOR' + } + self.params() + + clock_qos = qos.QoSProfile( + durability=qos.QoSDurabilityPolicy.VOLATILE, + reliability=qos.QoSReliabilityPolicy.BEST_EFFORT, + history=qos.QoSHistoryPolicy.KEEP_LAST, + depth=5) + self.clock_sub = self.create_subscription(Clock, '/clock', self.clock_callback, clock_qos) + self.status_pub = self.create_publisher(ScenarioStatusMsg, self.scenario_status_topic, 10) + + # self.request_bt_publishing() + + self.snapshot_sub = self.create_subscription( + BehaviourTree, + self.bt_topic, + self.snapshot_callback, + 10 + ) + + def clock_callback(self, msg): + self.time = msg + + def request_bt_publishing(self): + """Request publishing of the py-trees-behaviour tree via the given + topic name vy calling the corresponding service. + :returns: - + + """ + # self.client_node = rclpy.create_node('srv_client_node') + snapshot_client = self.create_client(OpenSnapshotStream, self.snapshot_srv_name) + while not snapshot_client.wait_for_service(timeout_sec=1.0): + self.logger.info( + 'Service to open bt snapshot not available, waiting again ...') + + req = OpenSnapshotStream.Request(topic_name=self.bt_topic) + req.topic_name = self.bt_topic + # calling service to publish pytrees behaviour tree snapshot to topic + future = snapshot_client.call_async(req) + rclpy.spin_until_future_complete(self, future) + if future.result() is not None: + self.bt_topic = future.result().topic_name + self.logger.info( + 'Triggered publishing of bt snapshot to topic: %s' % self.bt_topic) + else: + self.logger.error('Exception while calling service: {}'.format(future.exception())) + + def params(self): + """handle ROS parameters and store them as class variables + :returns: - + + """ + self.declare_parameter('bt_snapshot_topic', '/bt_snapshot', + descriptor=ParameterDescriptor(dynamic_typing=True)) + self.declare_parameter('snapshot_srv_name', '/scenario_execution/snapshot_streams/open', + descriptor=ParameterDescriptor(dynamic_typing=True)) + self.declare_parameter('scenario_status_topic', '/scenario_status', + descriptor=ParameterDescriptor(dynamic_typing=True)) + + self.bt_topic = self.get_parameter_or( + 'bt_snapshot_topic').get_parameter_value().string_value + self.snapshot_srv_name = self.get_parameter_or( + 'snapshot_srv_name').get_parameter_value().string_value + self.scenario_status_topic = self.get_parameter_or( + 'scenario_status_topic').get_parameter_value().string_value + + @staticmethod + def get_behaviour_infos(behaviour_tree_msg): + """get information about behaviour tree as dictionary + :returns: dictionary containing information + + """ + result = {} + for behaviour in behaviour_tree_msg.behaviours: + result[behaviour.name] = { + 'class_name': behaviour.class_name, + 'status': behaviour.status, + 'is_active': behaviour.is_active, + 'type': behaviour.type, + 'message': behaviour.message + } + return result + + def snapshot_callback(self, msg): + """callback for the behaviour tree snapshop topic + + :msg: incoming behaviour tree msg + :returns: - + + """ + current_time = None + if self.time: + current_time = self.time.clock + self.logger.debug('received bt_snapshot') + if self.scenario_bt is not None: + self.last_behaviour_infos = self.get_behaviour_infos( + self.scenario_bt + ) + self.scenario_bt = msg + self.behaviour_infos = self.get_behaviour_infos(self.scenario_bt) + # we can only check for changes if we have a previous bt available + if self.last_behaviour_infos is not None: + for behaviour, infos in self.behaviour_infos.items(): + if infos['status'] not in self.states_dict or self.last_behaviour_infos[behaviour]['status'] not in self.states_dict: + continue + if infos['status'] != self.last_behaviour_infos[behaviour]['status']: + beh_type = self.types_dict[infos['type']] + last_status = self.states_dict[self.last_behaviour_infos[behaviour]['status']] + current_status = self.states_dict[infos['status']] + result_str = f'{behaviour}({beh_type}): {last_status} > {current_status}' + debug_str = 'behaviour %s of type %s changed state from %s to %s, with message %s' % ( + behaviour, beh_type, last_status, current_status, infos['message']) + msg = ScenarioStatusMsg() + msg.data = result_str + msg.system_time = self.get_clock().now().to_msg() + if current_time: + msg.ros_time = current_time + self.logger.debug(debug_str) + self.status_pub.publish(msg) + + +def main(args=None): + """main function for the node to spin + """ + rclpy.init(args=args) + + scenario_status = ScenarioStatus() + + try: + scenario_status.request_bt_publishing() + rclpy.spin(scenario_status) + except KeyboardInterrupt: + pass + finally: + scenario_status.destroy_node() + + +if __name__ == "__main__": + main() diff --git a/tools/scenario_status/setup.cfg b/tools/scenario_status/setup.cfg new file mode 100644 index 00000000..b2f519b2 --- /dev/null +++ b/tools/scenario_status/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/scenario_status +[install] +install_scripts=$base/lib/scenario_status diff --git a/tools/scenario_status/setup.py b/tools/scenario_status/setup.py new file mode 100644 index 00000000..c19beb01 --- /dev/null +++ b/tools/scenario_status/setup.py @@ -0,0 +1,50 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Setup python package """ +import os +from glob import glob +from setuptools import find_packages, setup + +PACKAGE_NAME = 'scenario_status' + +setup( + name=PACKAGE_NAME, + version='0.0.1', + packages=find_packages(), + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + PACKAGE_NAME]), + ('share/' + PACKAGE_NAME, ['package.xml']), + (os.path.join('share', PACKAGE_NAME), glob('scenario_status/*.py')), + (os.path.join('share', PACKAGE_NAME), glob('params/*.yaml')), + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Simple node to call a service to publish the py-trees-\ + behaviour tree to a topic, then subscribe to that topic and publish \ + changes in behaviour states as strings at the time they are \ + happening.', + license='Apache License 2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'scenario_status_node = scenario_status.scenario_status_node:main' + ], + }, +) diff --git a/tools/tf_to_pose_publisher/README.md b/tools/tf_to_pose_publisher/README.md new file mode 100644 index 00000000..012a975a --- /dev/null +++ b/tools/tf_to_pose_publisher/README.md @@ -0,0 +1,3 @@ +# TF to Pose Publisher + +The `tf_to_pose_publisher` packages provides the tf_to_pose_publisher node that publishes a specified tf transform on a `PoseStamped` topic. \ No newline at end of file diff --git a/tools/tf_to_pose_publisher/launch/tf_to_pose_launch.py b/tools/tf_to_pose_publisher/launch/tf_to_pose_launch.py new file mode 100644 index 00000000..78edb412 --- /dev/null +++ b/tools/tf_to_pose_publisher/launch/tf_to_pose_launch.py @@ -0,0 +1,83 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, GroupAction +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node, PushRosNamespace + +ARGUMENTS = [ + DeclareLaunchArgument('namespace', default_value='', + description='Robot namespace (including leading /)'), + + DeclareLaunchArgument('base_frame_id', default_value='base_link', + description='base frame id of the robot'), + + DeclareLaunchArgument('ground_truth_frame_id', default_value='turtlebot4_base_link_gt', + description='ground truth frame id of the robot'), + +] + + +def generate_launch_description(): + namespace = LaunchConfiguration("namespace") + base_frame_id = LaunchConfiguration("base_frame_id") + ground_truth_frame_id = LaunchConfiguration("ground_truth_frame_id") + + base_link_pose_pub = GroupAction( + [ + PushRosNamespace( + namespace=namespace + ), + Node( + name='tf_to_pose_map_baselink', + package='tf_to_pose_publisher', + executable='tf_to_pose_publisher', + output='screen', + remappings=[('/tf', 'tf'), + ('/tf_static', 'tf_static'), + ('tf_as_pose', 'robot_pose_loc')], + parameters=[{ + 'child_frame_id': base_frame_id, + }] + ), + ] + ) + + base_link_ground_truth_pose_pub = GroupAction( + [ + PushRosNamespace( + namespace=namespace + ), + Node( + name='tf_to_pose_map_baselink_gt', + package='tf_to_pose_publisher', + executable='tf_to_pose_publisher', + output='screen', + remappings=[('/tf', 'tf'), + ('/tf_static', 'tf_static'), + ('tf_as_pose', 'robot_pose_gt')], + parameters=[{ + 'child_frame_id': ground_truth_frame_id, + }] + ), + ] + ) + + ld = LaunchDescription(ARGUMENTS) + ld.add_action(base_link_pose_pub) + ld.add_action(base_link_ground_truth_pose_pub) + return ld diff --git a/tools/tf_to_pose_publisher/package.xml b/tools/tf_to_pose_publisher/package.xml new file mode 100644 index 00000000..70c7f9bb --- /dev/null +++ b/tools/tf_to_pose_publisher/package.xml @@ -0,0 +1,18 @@ + + + + tf_to_pose_publisher + 1.0.0 + Publish a tf transform to a pose topic + Intel Labs + Intel Labs + Apache-2.0 + + rclpy + geometry_msgs + tf2_ros + + + ament_python + + diff --git a/tools/tf_to_pose_publisher/resource/tf_to_pose_publisher b/tools/tf_to_pose_publisher/resource/tf_to_pose_publisher new file mode 100644 index 00000000..e69de29b diff --git a/tools/tf_to_pose_publisher/setup.cfg b/tools/tf_to_pose_publisher/setup.cfg new file mode 100644 index 00000000..c8ead78a --- /dev/null +++ b/tools/tf_to_pose_publisher/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/tf_to_pose_publisher +[install] +install_scripts=$base/lib/tf_to_pose_publisher diff --git a/tools/tf_to_pose_publisher/setup.py b/tools/tf_to_pose_publisher/setup.py new file mode 100644 index 00000000..7926e26f --- /dev/null +++ b/tools/tf_to_pose_publisher/setup.py @@ -0,0 +1,45 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from glob import glob +import os +from setuptools import find_packages, setup + +PACKAGE_NAME = 'tf_to_pose_publisher' + +setup( + name=PACKAGE_NAME, + version='1.0.0', + packages=find_packages(exclude=['test']), + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + PACKAGE_NAME]), + ('share/' + PACKAGE_NAME, ['package.xml']), + (os.path.join('share', PACKAGE_NAME, 'launch'), glob('launch/*launch.py')) + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Intel Labs', + maintainer_email='scenario-execution@intel.com', + description='Publish a tf transform to a pose topic', + license='Apache License 2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'tf_to_pose_publisher = tf_to_pose_publisher.tf_to_pose_publisher:main', + ], + }, +) diff --git a/tools/tf_to_pose_publisher/tf_to_pose_publisher/__init__.py b/tools/tf_to_pose_publisher/tf_to_pose_publisher/__init__.py new file mode 100644 index 00000000..3ba13780 --- /dev/null +++ b/tools/tf_to_pose_publisher/tf_to_pose_publisher/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 diff --git a/tools/tf_to_pose_publisher/tf_to_pose_publisher/tf_to_pose_publisher.py b/tools/tf_to_pose_publisher/tf_to_pose_publisher/tf_to_pose_publisher.py new file mode 100644 index 00000000..2e17fc3b --- /dev/null +++ b/tools/tf_to_pose_publisher/tf_to_pose_publisher/tf_to_pose_publisher.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# Copyright (C) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + + +import rclpy +from rclpy.node import Node +from rclpy.callback_groups import MutuallyExclusiveCallbackGroup +from rcl_interfaces.msg import ParameterDescriptor + +from tf2_ros import TransformException # pylint: disable= no-name-in-module +from tf2_ros.buffer import Buffer +from tf2_ros.transform_listener import TransformListener + +from geometry_msgs.msg import PoseStamped +from geometry_msgs.msg import TransformStamped + + +class TfToPose(Node): + + def __init__(self): + super().__init__('tf_to_pose') + + self.parent_frame_id = None + self.child_frame_id = None + self.mode = None + self.last_transform = TransformStamped() + + self.params() + + timer_cb_group = MutuallyExclusiveCallbackGroup() + + self.tf_buffer = Buffer() + self.tf_listener = TransformListener(self.tf_buffer, self) + + timer_period = 0.1 + self._timer = self.create_timer(timer_period, self.timer_callback, callback_group=timer_cb_group) + + self.pose_pub = self.create_publisher(PoseStamped, 'tf_as_pose', 10) + + def params(self): + """handle ROS parameters and store them as class variables + :returns: - + + """ + self.declare_parameter('parent_frame_id', 'map', descriptor=ParameterDescriptor(dynamic_typing=True)) + self.parent_frame_id = self.get_parameter_or('parent_frame_id').get_parameter_value().string_value + + self.declare_parameter('child_frame_id', 'base_link', descriptor=ParameterDescriptor(dynamic_typing=True)) + self.child_frame_id = self.get_parameter_or('child_frame_id').get_parameter_value().string_value + + self.declare_parameter('mode', 'continuous', descriptor=ParameterDescriptor( + dynamic_typing=True)) # choose between continuous and onchange + self.mode = self.get_parameter_or('mode').get_parameter_value().string_value + + def timer_callback(self): + """timer callback funtion + :returns: - + + """ + try: + t = self.tf_buffer.lookup_transform( + self.parent_frame_id, + self.child_frame_id, + rclpy.time.Time() + ) + except TransformException as ex: + self.get_logger().debug( + f'Could not transform {self.parent_frame_id} to {self.child_frame_id}: {ex}') + return + + pose = PoseStamped() + pose.header = t.header + pose.header.frame_id = self.parent_frame_id + + pose.pose.position.x = t.transform.translation.x + pose.pose.position.y = t.transform.translation.y + pose.pose.orientation.x = t.transform.rotation.x + pose.pose.orientation.y = t.transform.rotation.y + pose.pose.orientation.z = t.transform.rotation.z + pose.pose.orientation.w = t.transform.rotation.w + + if self.mode == 'continuous': + # continuously publish the transform we looked up + self.pose_pub.publish(pose) + + elif self.mode == 'onchange': + self.get_logger().debug('tf to pose node is in mode onchanged') + changed = False + if t.transform != self.last_transform.transform: + changed = True + + if changed: + self.get_logger().debug('transform has changed, so we publish') + self.last_transform = t + # only publish transform if it changed + self.pose_pub.publish(pose) + else: + raise ValueError(f"Wrong value for mode: {self.mode}! Possible options are 'continuous' or 'onchange'") + + +def main(args=None): + rclpy.init(args=args) + tf_to_pose_node = TfToPose() + + executor = rclpy.executors.MultiThreadedExecutor() + executor.add_node(tf_to_pose_node) + + try: + executor.spin() + except KeyboardInterrupt: + tf_to_pose_node.get_logger().info("User requested shut down.") + finally: + rclpy.shutdown() + + +if __name__ == '__main__': + main()