Skip to content

Commit 9907260

Browse files
authored
Merge pull request #119 from dobots/Ros2_scalable
Ros2 scalable
2 parents bb48e28 + c4c4cfc commit 9907260

File tree

5 files changed

+629
-6
lines changed

5 files changed

+629
-6
lines changed

jupyros/ros2/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
from .._version import __version__
1010

11-
from ..ros1.ipy import *
12-
from ..ros1.pubsub import *
13-
from ..ros1.ros_widgets import *
14-
from ..ros1.ros3d import *
15-
from ..ros1.server_extension import *
16-
from ..ros1.turtle_sim import *
11+
#from ..ros1.ipy import *
12+
#from ..ros1.ros3d import *
13+
#from ..ros1.server_extension import *
14+
#from ..ros1.turtle_sim import *
15+
16+
from ..ros2.publisher import *
17+
from ..ros2.ros_widgets import *
18+
from ..ros2.subscriber import *

jupyros/ros2/publisher.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
"""
2+
Publisher class for jupyter-ros2 Project
3+
4+
Original Author: zmk5 (Zahi Kakish)
5+
6+
Adjusted by: ldania (Luigi Dania)
7+
Date: 19 July 2022
8+
9+
"""
10+
11+
from typing import TypeVar
12+
import threading
13+
import time
14+
import ipywidgets as widgets
15+
from .ros_widgets import add_widgets
16+
import functools
17+
18+
def rsetattr(obj, attr, val):
19+
pre, _, post = attr.rpartition('.')
20+
return setattr(rgetattr(obj, pre) if pre else obj, post, val)
21+
22+
# using wonder's beautiful simplification: https://stackoverflow.com/questions/31174295/getattr-and-setattr-on-nested-objects/31174427?noredirect=1#comment86638618_31174427
23+
24+
def rgetattr(obj, attr, *args):
25+
def _getattr(obj, attr):
26+
return getattr(obj, attr, *args)
27+
return functools.reduce(_getattr, [obj] + attr.split('.'))
28+
29+
30+
try:
31+
import rclpy
32+
from rclpy.node import Node
33+
except ModuleNotFoundError:
34+
print("The rclpy package is not found in your $PYTHONPATH. " +
35+
"Subscribe and publish are not going to work.")
36+
print("Do you need to activate your ros2 environment?")
37+
38+
39+
# Used for documentation purposes only
40+
MsgType = TypeVar('MsgType')
41+
42+
43+
class Publisher():
44+
"""
45+
Creates a class containing the form widget for message type `msg_type`.
46+
This class analyzes the fields of msg_type and creates
47+
an appropriate widget.
48+
49+
A ros2 publisher is automatically created which publishes to the
50+
topic given as topic parameter. This allows pressing the
51+
"Send Message" button to send the message.
52+
53+
:param node: An rclpy node class to attach to the publisher.
54+
:param msg_type: The message type to publish.
55+
:param topic: The topic name on which to publish the message.
56+
57+
"""
58+
def __init__(self, node: Node, msg_type: MsgType, topic: str) -> None:
59+
# Check if a ros2 node is provided.
60+
if (not isinstance(node, Node) or not issubclass(type(node), Node)):
61+
raise TypeError(
62+
"Input argument 'node' is not of type rclpy.node.Node!")
63+
64+
# Check if topic already created.
65+
for operating_publisher in node.publishers:
66+
if topic[0] != "/":
67+
if "/" + topic is operating_publisher.topic:
68+
raise AttributeError(
69+
f"Publisher for topic, /{topic}, already created!")
70+
71+
if topic is operating_publisher.topic:
72+
raise AttributeError(
73+
f"Publisher for topic, {topic}, already created!")
74+
75+
# Set initial node and widget variables.
76+
self.node = node
77+
self.topic = topic
78+
self.msg_type = msg_type
79+
self.__publisher = self.node.create_publisher(msg_type, topic, 10)
80+
self.__thread_map = {}
81+
self.__widget_list = []
82+
self.__widget_dict = {}
83+
self.__widgets = {
84+
"rate_field": widgets.IntText(description="Rate", value=5),
85+
"stop_btn": widgets.Button(description="Start"),
86+
"send_btn": widgets.Button(description="Send Message"),
87+
"txt_input": widgets.Text(description="Message", value="Something")
88+
}
89+
90+
self.widget_dict, self.widget_list = add_widgets(self.msg_type, self.__widget_dict, self.__widget_list)
91+
92+
def widget_dict_to_msg(self):
93+
94+
"""
95+
Iterate over the attributes and give them per attribute
96+
97+
98+
"""
99+
for key in self.__widget_list:
100+
if(key.has_trait('children')):
101+
try:
102+
attr_adress = ".".join([head_class, str(key.children[0].value)])
103+
print(attr_adress)
104+
#rsetattr(bun,attr_adress, 0.0)
105+
rsetattr(self.msg_inst, attr_adress, float(key.children[1].value))
106+
except:
107+
next
108+
else:
109+
head_class = key.value
110+
111+
112+
#submsg = getattr(msg_instance, key)
113+
#self._sub_msg[key] =
114+
#widget_dict_to_msg(submsg, d[key])
115+
116+
117+
118+
def display(self) -> widgets.VBox:
119+
""" Display's widgets within the Jupyter Cell for a ros2 Publisher """
120+
self.__widgets["send_btn"].on_click(self.__send_msg)
121+
self.__widgets["stop_btn"].on_click(self.__start_thread)
122+
top_box = widgets.HBox((
123+
self.__widgets["txt_input"],
124+
))
125+
btm_box = widgets.HBox((
126+
self.__widgets["send_btn"],
127+
self.__widgets["rate_field"],
128+
self.__widgets["stop_btn"],
129+
130+
))
131+
self.__widget_list.append(btm_box)
132+
vbox = widgets.VBox(children=self.__widget_list)
133+
134+
return vbox
135+
136+
137+
138+
def __send_msg(self, args):
139+
140+
141+
""" Generic call to send message. """
142+
self.msg_inst = self.msg_type()
143+
self.widget_dict_to_msg()
144+
self.__publisher.publish(self.msg_inst)
145+
#self.__publisher.publish()
146+
147+
148+
149+
def __thread_target(self) -> None:
150+
d = 1.0 / float(self.__widgets["rate_field"].value)
151+
while self.__thread_map[self.topic]:
152+
self.__send_msg(None)
153+
time.sleep(d)
154+
155+
def __start_thread(self, _) -> None:
156+
self.__thread_map[self.topic] = not self.__thread_map[self.topic]
157+
if self.__thread_map[self.topic]:
158+
local_thread = threading.Thread(target=self.__thread_target)
159+
local_thread.start()
160+
self.__widgets["stop_btn"].description = "Stop"
161+
else:
162+
self.__widgets["stop_btn"].description = "Start"
163+

jupyros/ros2/ros_widgets.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import rosidl_runtime_py.utilities as rut
2+
import ipywidgets as widgets
3+
from ament_index_python.packages import get_package_share_directory
4+
import rosidl_runtime_py.utilities as rut
5+
6+
7+
8+
9+
10+
11+
12+
def add_widgets(msg_instance, widget_dict, widget_list, prefix=''):
13+
"""
14+
Adds widgets.
15+
16+
@param msg_type The message type
17+
@param widget_dict The form list
18+
@param widget_list The widget list
19+
20+
@return widget_dict and widget_list
21+
"""
22+
# import only here so non ros env doesn't block installation
23+
#from genpy import Message
24+
"""
25+
if msg_instance._type.split('/')[-1] == 'Image':
26+
w = widgets.Text()
27+
widget_dict['img'] = w
28+
w_box = widgets.HBox([widgets.Label(value='Image path:'), w])
29+
widget_list.append(w_box)
30+
return widget_dict, widget_list
31+
"""
32+
if(rut.is_message(msg_instance) == False):
33+
return 0
34+
## Type of msg
35+
#msg_class = msg_instance._fields_and_field_types
36+
37+
38+
39+
for idx, slot in enumerate(msg_instance._fields_and_field_types):
40+
41+
42+
43+
try:
44+
msg_inst = msg_instance()
45+
except:
46+
msg_inst = msg_instance
47+
attr = getattr(msg_inst, slot)
48+
49+
#s_t = msg_instance._fields_and_field_types[slot]
50+
51+
try:
52+
msg_attr = attr.get_fields_and_field_types()
53+
except:
54+
next
55+
56+
w = None
57+
58+
if(rut.is_message(msg_instance)):
59+
widget_list.append(widgets.Label(value=slot))
60+
61+
widget_dict[slot] = {}
62+
63+
for s_t in msg_attr:
64+
65+
66+
if msg_attr[s_t] in ['float', 'float32', 'float64', 'double']:
67+
w = widgets.FloatText()
68+
69+
if msg_attr[s_t] in ['int', 'int8', 'uint8', 'int32', 'uint32', 'int64', 'uint64']:
70+
w = widgets.IntText()
71+
72+
if msg_attr[s_t] in ['string']:
73+
w = widgets.Text()
74+
75+
76+
77+
if(w):
78+
79+
widget_dict[slot] = w
80+
w_box = widgets.HBox([widgets.Label(value=s_t, layout=widgets.Layout(width="100px")), w])
81+
widget_list.append(w_box)
82+
83+
return widget_dict, widget_list
84+
85+
86+
87+
88+
89+
90+
"""
91+
if(rut.is_message(msg_instance)):
92+
93+
widget_list.append(widgets.Label(value=slot))
94+
95+
widget_dict[slot] = {}
96+
else:
97+
print("No inst")
98+
"""

0 commit comments

Comments
 (0)