Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add/ros actions in scxml #30

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
406c842
Add support to BT Input Ports
MarcoLm993 Aug 19, 2024
ed0dfd5
Implement scxml action interface. No conversion to Jani yet
MarcoLm993 Aug 26, 2024
ded0f7e
Add array support in scxml and jani
MarcoLm993 Aug 28, 2024
15c4265
Add missing field to thread start and start action diagram
MarcoLm993 Aug 28, 2024
18acac9
Complete overview of action conversion to plain scxml
MarcoLm993 Aug 29, 2024
6cee7a5
Refactor ros service handler for reusability
MarcoLm993 Aug 30, 2024
b58a8b9
Prepare foundation for Ros Action Handler conversion
MarcoLm993 Aug 30, 2024
c21e61d
Implement goal_request transition
MarcoLm993 Aug 30, 2024
9b1a91c
Goal accepted and rejected transitions
MarcoLm993 Aug 30, 2024
c46314c
Switch names in event generator function and finish first implementat…
MarcoLm993 Aug 30, 2024
ee5efe6
First untested integration of action handler
MarcoLm993 Aug 30, 2024
2d2513c
Integrate fibonacci jani conversion test
MarcoLm993 Aug 30, 2024
384d983
Initial set of bugfixes
MarcoLm993 Aug 30, 2024
2358217
Avoid else body in ScxmlIf to be None
MarcoLm993 Aug 30, 2024
0f6c410
Hacky solution for array and more fixes
MarcoLm993 Aug 30, 2024
5615a44
First running jani file. To be debugged
MarcoLm993 Aug 30, 2024
ed3afe3
SCXML adjustments
MarcoLm993 Sep 2, 2024
7d2c402
Temp single thread test for action
MarcoLm993 Sep 2, 2024
2a72863
Fixed bug causing state name overlap
MarcoLm993 Sep 2, 2024
1c1536a
Enable multi-threaded action test
MarcoLm993 Sep 2, 2024
eed223d
Add intermediate state in handler for action goal request
MarcoLm993 Sep 2, 2024
2957188
Fix problem in action server SCXML model
MarcoLm993 Sep 2, 2024
5020fe4
Fix action test and add todo
MarcoLm993 Sep 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
use-pydocstyle: false
extra-pylint-options: ""
extra-pycodestyle-options: ""
extra-flake8-options: ""
extra-flake8-options: "--max-line-length=100"
extra-black-options: ""
extra-mypy-options: "--ignore-missing-imports"
extra-isort-options: ""
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- name: Get smc_storm
id: get_smc_storm
run: |
wget https://github.com/convince-project/smc_storm/releases/download/0.0.2/smc_storm_executable.tar.gz
wget https://github.com/convince-project/smc_storm/releases/download/0.0.3/smc_storm_executable.tar.gz
tar -xzf smc_storm_executable.tar.gz
./install.sh --install-dependencies
# Save the path to the smc_storm executable
Expand Down
80 changes: 53 additions & 27 deletions as2fm_common/src/as2fm_common/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,28 @@
Common functionalities used throughout the toolchain.
"""

from typing import Union
from typing import get_args, MutableSequence, Union, Type
from array import array

ValidTypes = Union[bool, int, float]
"""We define the basic types that are supported by the Jani language:
"""
Set of basic types that are supported by the Jani language.

// Types
// We cover only the most basic types at the moment.
// In the remainder of the specification, all requirements like "y must be of type x" are to be interpreted
// as "type x must be assignable from y's type".
Basic types (from Jani docs):
Types
We cover only the most basic types at the moment.
In the remainder of the specification, all requirements like "y must be of type x" are to be
interpreted as "type x must be assignable from y's type".
var BasicType = schema([
"bool", // assignable from bool
"int", // numeric; assignable from int and bounded int
"real" // numeric; assignable from all numeric types
]);
src https://docs.google.com/document/d/\
1BDQIzPBtscxJFFlDUEPIo8ivKHgXT8_X6hz5quq7jK0/edit"""


def ros_type_name_to_python_type(type_str: str) -> type:
"""Convert a string representing a type to a python type.

Source: https://docs.ros.org/en/rolling/Concepts/Basic/\
About-Interfaces.html#field-types
1BDQIzPBtscxJFFlDUEPIo8ivKHgXT8_X6hz5quq7jK0/edit

:param type_str: The string representing the type
:return: The python type
"""
if type_str in ['bool', 'boolean']:
return bool
if type_str in ['int8', 'int16', 'int32', 'int64',
'uint8', 'uint16', 'uint32', 'uint64']:
return int
if type_str in ['float32', 'float64']:
return float
raise NotImplementedError(f"Type {type_str} not supported.")
Additionally, we support the array types from the array extension.
"""
ValidTypes = Union[bool, int, float, MutableSequence[int], MutableSequence[float]]


def remove_namespace(tag: str) -> str:
Expand All @@ -67,4 +54,43 @@ def remove_namespace(tag: str) -> str:
tag_wo_ns = tag.split('}')[-1]
else:
tag_wo_ns = tag
return tag_wo_ns
return tag_wo_ns


def get_default_expression_for_type(field_type: Type[ValidTypes]) -> ValidTypes:
"""Generate a default expression for a field type."""
assert field_type in get_args(ValidTypes), f"Error: Unsupported data type {field_type}."
if field_type is MutableSequence[int]:
return array('i')
elif field_type is MutableSequence[float]:
return array('d')
else:
return field_type()


def value_to_type(value: ValidTypes) -> Type[ValidTypes]:
"""Convert a value to a type."""
if isinstance(value, array):
if value.typecode == 'i':
return MutableSequence[int]
elif value.typecode == 'd':
return MutableSequence[float]
else:
raise ValueError(f"Type of array '{value.typecode}' not supported.")
elif isinstance(value, (int, float, bool)):
return type(value)
else:
raise ValueError(f"Unsupported value type {type(value)}.")


def value_to_string(value: ValidTypes) -> str:
"""Convert a value to a string."""
if isinstance(value, MutableSequence):
# Expect value to be an array
return f'[{",".join(str(v) for v in value)}]'
elif isinstance(value, bool):
return str(value).lower()
elif isinstance(value, (int, float)):
return str(value)
else:
raise ValueError(f"Unsupported value type {type(value)}.")
32 changes: 28 additions & 4 deletions as2fm_common/src/as2fm_common/ecmascript_interpretation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,46 @@
Module for interpreting ecmascript.
"""

from typing import Dict, Optional
from typing import Dict, Optional, Union
from array import array

import js2py

from as2fm_common.common import ValidTypes


BASIC_JS_TYPES = Union[int, float, bool]


def interpret_ecma_script_expr(
expr: str, variables: Optional[Dict[str, ValidTypes]] = None) -> object:
"""Interpret the ECMA script expression.
"""
Interpret the ECMA script expression.

:param expr: The ECMA script expression
:return: The interpreted object
"""
if variables is None:
variables = {}
context = js2py.EvalJs(variables)
context.execute("result = " + expr)
return context.result
try:
context.execute("result = " + expr)
except js2py.base.PyJsException:
msg_addition = ""
if expr in ("True", "False"):
msg_addition = "Did you mean to use 'true' or 'false' instead?"
raise RuntimeError(f"Failed to interpret JS expression: 'result = {expr}'. {msg_addition}")
expr_result = context.result
if isinstance(expr_result, BASIC_JS_TYPES):
return expr_result
elif isinstance(expr_result, js2py.base.JsObjectWrapper):
if isinstance(expr_result._obj, js2py.base.PyJsArray):
return expr_result.to_list()
else:
raise ValueError(f"Expected expr. {expr} to be of type {BASIC_JS_TYPES} or "
f"an array, got '{type(expr_result._obj)}'")
elif isinstance(expr_result, array):
return expr_result
else:
raise ValueError(f"Expected expr. {expr} to be of type {BASIC_JS_TYPES} or "
f"JsObjectWrapper, got '{type(expr_result)}'")
56 changes: 56 additions & 0 deletions as2fm_common/test/test_unittest_ecmascript_interpretation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright (c) 2024 - for information on the respective copyright owner
# see the NOTICE file

# 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.

""""Test the SCXML data conversion"""

import unittest
import pytest

from as2fm_common.ecmascript_interpretation import interpret_ecma_script_expr


class TestEcmascriptInterpreter(unittest.TestCase):

def test_ecmascript_types(self):
"""
Test with ECMAScript expression that evaluates to different types.

src https://alexzhornyak.github.io/SCXML-tutorial/Doc/\
datamodel.html#ecmascript
"""
self.assertEqual(interpret_ecma_script_expr("1"), 1)
self.assertEqual(interpret_ecma_script_expr("1.1"), 1.1)
self.assertEqual(interpret_ecma_script_expr("true"), True)
self.assertEqual(interpret_ecma_script_expr("false"), False)
self.assertEqual(interpret_ecma_script_expr("[1,2,3]"), [1, 2, 3])

def test_ecmascript_unsupported(self):
"""
Test with ECMA script expressions that evaluates to unsupported types.

This should raise a ValueError because the types are not supported
by Jani.

src https://alexzhornyak.github.io/SCXML-tutorial/Doc/\
datamodel.html#ecmascript
"""
self.assertRaises(ValueError, interpret_ecma_script_expr, "\'this is a string\'")
self.assertRaises(ValueError, interpret_ecma_script_expr, "null")
self.assertRaises(ValueError, interpret_ecma_script_expr, "undefined")
self.assertRaises(ValueError, interpret_ecma_script_expr, "new Date()")


if __name__ == '__main__':
pytest.main(['-s', '-v', __file__])
2 changes: 1 addition & 1 deletion as2fm_common/test/test_utilities_smc_storm.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def _run_smc_storm(args: str):

def test_run_smc_storm():
"""Testing if it is possible to run smc_storm."""
result =_run_smc_storm("-v")
result = _run_smc_storm("-v")
assert result, "smc_storm failed to run"


Expand Down
Loading