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

Port & blackboard support #23

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c6e9c65
Example for blackboard and ports
ct2034 Aug 14, 2024
432b22d
port barebones
ct2034 Aug 14, 2024
8251a47
Refine the bt_events replacement methods
MarcoLm993 Aug 15, 2024
e2f3c9a
Refactor bt_converter. Tests to be fixed
MarcoLm993 Aug 15, 2024
7c6a6f7
Fix replacement in send and transition classes
MarcoLm993 Aug 15, 2024
66ac110
Fix scxml_converter unit test
MarcoLm993 Aug 15, 2024
df29dac
Adjust test files structure in scxml_converter
MarcoLm993 Aug 15, 2024
b619343
Move bt conversion test to separated function
MarcoLm993 Aug 15, 2024
72e890b
Add option to store test data
MarcoLm993 Aug 15, 2024
550fa74
Prepare test for bt ports
MarcoLm993 Aug 15, 2024
ef3932e
Rename bt file to correct format
MarcoLm993 Aug 15, 2024
812a94e
Split utils in ros, bt and normal ones
MarcoLm993 Aug 15, 2024
84d206d
Implement container to track BT ports values
MarcoLm993 Aug 15, 2024
60b34bd
Parse port declarations in scxml
MarcoLm993 Aug 15, 2024
e985a99
lint
MarcoLm993 Aug 15, 2024
58bec75
Prototypical xml parsing for new publisher declaration
MarcoLm993 Aug 15, 2024
b27af69
Prepare the ros declaration handler to support aliases for ROS entries
MarcoLm993 Aug 16, 2024
42b7b57
Lint
MarcoLm993 Aug 16, 2024
00e427d
Prepare interface in scxml_root for the BT ports
MarcoLm993 Aug 16, 2024
c4daaae
Integrate bt ports in bt_converter
MarcoLm993 Aug 16, 2024
4a75405
Fix bugs preventing parsing of topic name from bt ports
MarcoLm993 Aug 16, 2024
41c1c75
Implement various bt_ports update methods for scxml state children
MarcoLm993 Aug 16, 2024
5286f5d
First version with ports
MarcoLm993 Aug 16, 2024
4b53971
fix scxml_converter tests
MarcoLm993 Aug 16, 2024
b8c5e1c
Port new code structure to topic subscriber, too
MarcoLm993 Aug 16, 2024
3f4879c
Exmplicitly handle strings in scxml children parsing
MarcoLm993 Aug 19, 2024
fd10659
Sanitize event names in test properties
MarcoLm993 Aug 19, 2024
738b0c5
Handle BT-declared service names in declaration
MarcoLm993 Aug 19, 2024
7bfab17
Handle dynamic service names for service functionalities in scxml
MarcoLm993 Aug 19, 2024
37b9aa3
Additional test for BT ports
MarcoLm993 Aug 19, 2024
6450bd8
Integrate Bt ports in Scxml Params and ROS Fields
MarcoLm993 Aug 19, 2024
80ac6b3
Integrate BT ports in datamodel, too
MarcoLm993 Aug 19, 2024
99da469
Update ROS topic documentation for ROS topics and Services
MarcoLm993 Aug 19, 2024
a77d77d
Update documentation with BT Ports examples
MarcoLm993 Aug 19, 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
86 changes: 71 additions & 15 deletions docs/source/howto.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,14 @@ ROS Topics are used to publish (via a ROS Publisher) and receive (via a ROS Subs
.. code-block:: xml

<!-- ROS Topic Subscriber -->
<ros_topic_subscriber topic="/topic1" type="std_msgs/Bool" />
<ros_topic_subscriber name="bool_topic" topic="/topic1" type="std_msgs/Bool" />
<!-- ROS Topic Publisher -->
<ros_topic_publisher topic="/topic2" type="std_msgs/Int32" />
<ros_topic_publisher name="int_topic" topic="/topic2" type="std_msgs/Int32" />

Once created, subscribers and publishers can be referenced using the `topic` name, and can be used in the states to send messages and perform callbacks upon receiving messages:
The two declarations above will create a ROS subscriber called `bool_topic` that reads messages of type `std_msgs/Bool` from the topic `/topic1` and a ROS publisher called `int_topic` that writes messages of type `std_msgs/Int32` on the topic `/topic2`.
The `name` argument is optional, and if not provided, it will be set to the same value as the `topic` argument.

Once created, subscribers and publishers can be referenced using their names (`bool_topic` and `int_topic`), and can be used in the states to send messages and perform callbacks upon receiving messages:

.. code-block:: xml

Expand All @@ -117,19 +120,19 @@ Once created, subscribers and publishers can be referenced using the `topic` nam
</datamodel>

<state id="src_state">
<ros_topic_callback topic="/topic1" target="target_state">
<ros_topic_callback name="bool_topic" target="target_state">
<assign location="internal_var" expr="_msg.data" />
</ros_topic_callback>
</state>

<state id="target_state">
<onentry>
<if cond="internal_bool">
<ros_topic_publish topic="/topic2" >
<ros_topic_publish name="int_topic" >
<field name="data" expr="10">
</ros_topic_publish>
<else />
<ros_topic_publish topic="/topic2" >
<ros_topic_publish name="int_topic" >
<field name="data" expr="20">
</ros_topic_publish>
</if>
Expand All @@ -154,11 +157,11 @@ The declaration of a ROS Service server and the one of a client can be achieved
.. code-block:: xml

<!-- ROS Service Server -->
<ros_service_server service_name="/service1" type="std_srvs/SetBool" />
<ros_service_server name="the_srv" service_name="/service1" type="std_srvs/SetBool" />
<!-- ROS Service Client -->
<ros_service_client service_name="/service2" type="std_srvs/Trigger" />
<ros_service_client name="the_client" service_name="/service2" type="std_srvs/Trigger" />

Once created, servers and clients can be referenced using the `service_name` name, and can be used in the states of a SCXML model to provide and request services.
Once created, servers and clients can be referenced using the provided `name` (i.e. `the_srv` and `the_client`), and can be used in the states of a SCXML model to provide and request services.
In the following, an exemplary client is provided:

.. code-block:: xml
Expand All @@ -169,16 +172,16 @@ In the following, an exemplary client is provided:

<state id="send_req">
<onentry>
<ros_service_send_request service_name="/service2">
<ros_service_send_request name="the_client">
</ros_service_send_request>
</onentry>
<ros_service_handle_response service_name="/service2" target="done">
<ros_service_handle_response name="the_client" target="done">
<assign location="internal_bool" expr="_res.success" />
</ros_service_handle_response>
</state>

To send a request, the `ros_service_send_request` can be used where any other executable content may be used.
After the server has processed the service, `ros_service_handle_response`, can be used similarly to a SCXML transition and is triggered by the server.
After the server has processed the service, `ros_service_handle_response`, can be used similarly to a SCXML transition and is triggered when a response from the server is received.
The data of the request can be accessed with the `_res` field.

And here, an example of a server:
Expand All @@ -190,9 +193,9 @@ And here, an example of a server:
</datamodel>

<state id="idle">
<ros_service_handle_request service_name="/service1" target="idle">
<ros_service_handle_request name="the_srv" target="idle">
<assign location="temp_data" expr="_req.data" />
<ros_service_send_response service_name="/adder">
<ros_service_send_response name="the_srv">
<field name="success" expr="temp_data" />
</ros_service_send_response>
</ros_service_handle_request>
Expand All @@ -215,7 +218,60 @@ TODO
Creating a SCXML model of a BT plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

TODO
SCXML models of BT plugins can be done similarly to the ones for ROS nodes. However, in BT plugins there are a few special functionalities that are provided:

* :ref:`BT communication <bt_communication>`: A set of special events that are used in each BT plugins for starting a BT Node and provide results.
* :ref:`BT Ports <bt_ports>`: A special BT interface to parametrize a specific plugin instance.


.. _bt_communication:

BT Communication
_________________

TODO: describe `bt_tick`, `bt_running`, `bt_success`, `bt_failure`.


.. _bt_ports:

BT Ports
________

Additionally, when loading a BT plugin in the BT XML Tree, it is possible to configure a specific plugin instance by means of the BT ports.

As in the case of ROS functionalities, BT Ports need to be declared before being used, to provide the port name and expected type.

.. code-block:: xml

<bt_port key="my_string_port" type="string" />
<bt_port key="start_value" type="int32">

Once declared, it is possible to reference to the port in multiple SCXML entries.

For example, we can use `my_string_port` to define the topic used by a ROS publisher.

.. code-block:: xml

<ros_topic_publisher name="int_topic" type="std_msgs/Int32">
<topic>
<bt_get_input key="my_string_port" />
</topic>
</ros_topic_publisher>

Or we can use `start_value` to define the initial value of a variable.

.. code-block:: xml

<datamodel>
<data id="counter" type="int32">
<expr>
<bt_get_input key="start_value" />
</expr>
</data>
</datamodel>


BT ports can also be linked to variables in the `BT Blackboard` by wrapping the variable name in curly braces in the BT xml file. However, this feature is not yet supported.


.. _additional_params_howto:
Expand Down
9 changes: 4 additions & 5 deletions jani_generator/src/jani_generator/ros_helpers/ros_services.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2024 - for information on the respective copyright owner

Check failure on line 1 in jani_generator/src/jani_generator/ros_helpers/ros_services.py

View workflow job for this annotation

GitHub Actions / jani_generator ⏩ isort

Imports are incorrectly sorted and/or formatted.
# see the NOTICE file

# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -24,11 +24,10 @@
ScxmlDataModel, ScxmlParam,
ScxmlRoot, ScxmlSend, ScxmlState,
ScxmlTransition)
from scxml_converter.scxml_entries.utils import (
generate_srv_request_event, generate_srv_response_event,
generate_srv_server_request_event, generate_srv_server_response_event,
get_default_expression_for_type, get_srv_type_params,
sanitize_ros_interface_name)
from scxml_converter.scxml_entries.utils import get_default_expression_for_type
from scxml_converter.scxml_entries.ros_utils import (
generate_srv_request_event, generate_srv_response_event, generate_srv_server_request_event,
generate_srv_server_response_event, get_srv_type_params, sanitize_ros_interface_name)

SRV_PREFIX = "srv_handler_"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@
import json
import os
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Tuple, Union
from typing import List, Optional, Tuple
from xml.etree import ElementTree as ET

from as2fm_common.common import remove_namespace
from jani_generator.jani_entries import JaniModel
from jani_generator.ros_helpers.ros_services import RosService, RosServices
from jani_generator.ros_helpers.ros_timer import RosTimer
from jani_generator.scxml_helpers.scxml_to_jani import \
Expand Down Expand Up @@ -129,34 +128,33 @@ def generate_plain_scxml_models_and_timers(
"""
Generate plain SCXML models and ROS timers from the full model dictionary.
"""
# Convert behavior tree and plugins to ROS-scxml
# Load the skills and components scxml files (ROS-SCXML)
scxml_files_to_convert: list = model.skills + model.components
ros_scxmls: List[ScxmlRoot] = []
for fname in scxml_files_to_convert:
ros_scxmls.append(ScxmlRoot.from_scxml_file(fname))
# Convert behavior tree and plugins to ROS-SCXML
if model.bt is not None:
bt_out_dir = os.path.join(os.path.dirname(model.bt), "generated_bt_scxml")
os.makedirs(bt_out_dir, exist_ok=True)
expanded_bt_plugin_scxmls = bt_converter(
model.bt, model.plugins, bt_out_dir)
scxml_files_to_convert.extend(expanded_bt_plugin_scxmls)

# Convert ROS-SCXML FSMs to plain SCXML
ros_scxmls.extend(bt_converter(model.bt, model.plugins))
# Convert the loaded entries to plain SCXML
plain_scxml_models = []
all_timers: List[RosTimer] = []
all_services: RosServices = {}
for fname in scxml_files_to_convert:
for scxml_entry in ros_scxmls:
plain_scxml, ros_declarations = \
ScxmlRoot.from_scxml_file(fname).to_plain_scxml_and_declarations()
scxml_entry.to_plain_scxml_and_declarations()
# Handle ROS timers
for timer_name, timer_rate in ros_declarations._timers.items():
assert timer_name not in all_timers, \
f"Timer {timer_name} already exists."
all_timers.append(RosTimer(timer_name, timer_rate))
# Handle ROS Services
for service_name, service_type in ros_declarations._service_clients.items():
for service_name, service_type in ros_declarations._service_clients.values():
if service_name not in all_services:
all_services[service_name] = RosService()
all_services[service_name].append_service_client(
service_name, service_type, plain_scxml.get_name())
for service_name, service_type in ros_declarations._service_servers.items():
for service_name, service_type in ros_declarations._service_servers.values():
if service_name not in all_services:
all_services[service_name] = RosService()
all_services[service_name].set_service_server(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
"op": "∧",
"left": {
"op": "<",
"left": "ros_topic./sender_a_counter.data",
"left": "ros_topic.sender_a_counter.data",
"right": 100
},
"right": {
"op": "∧",
"left": {
"op": "<",
"left": "ros_topic./sender_b_counter.data",
"left": "ros_topic.sender_b_counter.data",
"right": 100
},
"right": {
"op": "<",
"left": "ros_topic./receiver_counter.data",
"left": "ros_topic.receiver_counter.data",
"right": 100
}
}
Expand All @@ -35,16 +35,16 @@
"op": "∧",
"left": {
"op": ">",
"left": "ros_topic./receiver_counter.data",
"left": "ros_topic.receiver_counter.data",
"right": 48
},
"right": {
"op": "=",
"left": 50,
"right": {
"op": "+",
"left": "ros_topic./sender_a_counter.data",
"right": "ros_topic./sender_b_counter.data"
"left": "ros_topic.sender_a_counter.data",
"right": "ros_topic.sender_b_counter.data"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
"op": "∧",
"left": {
"op": "∧",
"left": "ros_topic./client_1_res.data",
"right": "ros_topic./client_1_res.valid"
"left": "ros_topic.client_1_res.data",
"right": "ros_topic.client_1_res.valid"
},
"right": {
"op": "∧",
"left": "ros_topic./client_2_res.data",
"right": "ros_topic./client_2_res.valid"
"left": "ros_topic.client_2_res.data",
"right": "ros_topic.client_2_res.valid"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion jani_generator/test/test_systemtest_scxml_to_jani.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def test_with_main_fail(self):
def test_with_w_bt_main_battery_depleted(self):
"""Here we expect the property to be *not* satisfied."""
# TODO: Improve properties under evaluation!
self._test_with_main('ros_example_w_bt', 'battery_depleted', False)
self._test_with_main('ros_example_w_bt', 'battery_depleted', False, True)

def test_with_w_bt_main_battery_under_twenty(self):
"""Here we expect the property to be *not* satisfied."""
Expand Down
Loading
Loading