Skip to content

Commit 4ec7cab

Browse files
ROS2 version
1 parent 7914c3a commit 4ec7cab

34 files changed

+573
-832
lines changed

CMakeLists.txt

Lines changed: 0 additions & 11 deletions
This file was deleted.

config/params.yaml

Lines changed: 0 additions & 1 deletion
This file was deleted.

launch/endpoint.launch

Lines changed: 0 additions & 4 deletions
This file was deleted.

launch/endpoint.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from launch import LaunchDescription
2+
from launch_ros.actions import Node
3+
4+
def generate_launch_description():
5+
return LaunchDescription([
6+
Node(
7+
package='ros2_tcp_endpoint',
8+
executable='default_server_endpoint',
9+
emulate_tty=True,
10+
parameters=[
11+
{'/ROS_IP': '0.0.0.0'},
12+
{'/ROS_TCP_PORT': 10000},
13+
],
14+
),
15+
])
16+

package.xml

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
<?xml version="1.0"?>
2-
<package format="2">
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
34
<name>ros_tcp_endpoint</name>
4-
<version>0.5.0</version>
5-
<description>Acts as the bridge between Unity messages sent via Websocket and ROS messages.</description>
6-
5+
<version>0.0.1</version>
6+
<description>ROS TCP Endpoint Unity Integration (ROS2 version)</description>
77
<maintainer email="unity-robotics@unity3d.com">Unity Robotics</maintainer>
8-
98
<license>Apache 2.0</license>
10-
<buildtool_depend>catkin</buildtool_depend>
11-
<build_depend>rospy</build_depend>
12-
<build_depend>message_generation</build_depend>
13-
<build_export_depend>rospy</build_export_depend>
14-
<exec_depend>rospy</exec_depend>
15-
<exec_depend>message_runtime</exec_depend>
169

10+
<test_depend>ament_copyright</test_depend>
11+
<test_depend>ament_flake8</test_depend>
12+
<test_depend>ament_pep257</test_depend>
13+
<test_depend>python3-pytest</test_depend>
1714

18-
<!-- The export tag contains other, unspecified, tags -->
1915
<export>
20-
<!-- Other tools can request additional information be placed here -->
21-
16+
<build_type>ament_python</build_type>
2217
</export>
2318
</package>

resource/ros_tcp_endpoint

Whitespace-only changes.

src/ros_tcp_endpoint/__init__.py renamed to ros_tcp_endpoint/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,4 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from .publisher import RosPublisher
16-
from .subscriber import RosSubscriber
17-
from .service import RosService
1815
from .server import TcpServer
19-
from .unity_service import UnityService

src/ros_tcp_endpoint/client.py renamed to ros_tcp_endpoint/client.py

Lines changed: 41 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import rclpy
1516
import struct
16-
import socket
17-
import rospy
18-
from io import BytesIO
1917

2018
import threading
2119
import json
2220

21+
from rclpy.serialization import deserialize_message
22+
from rclpy.serialization import serialize_message
23+
2324
from .exceptions import TopicOrServiceNameDoesNotExistError
2425

2526

@@ -28,7 +29,6 @@ class ClientThread(threading.Thread):
2829
Thread class to read all data from a connection and pass along the data to the
2930
desired source.
3031
"""
31-
3232
def __init__(self, conn, tcp_server, incoming_ip, incoming_port):
3333
"""
3434
Set class variables
@@ -66,11 +66,10 @@ def read_int32(conn):
6666
6767
"""
6868
raw_bytes = ClientThread.recvall(conn, 4)
69-
num = struct.unpack("<I", raw_bytes)[0]
69+
num = struct.unpack('<I', raw_bytes)[0]
7070
return num
7171

72-
@staticmethod
73-
def read_string(conn):
72+
def read_string(self):
7473
"""
7574
Reads int32 from socket connection to determine how many bytes to
7675
read to get the string that follows. Read that number of bytes and
@@ -79,39 +78,39 @@ def read_string(conn):
7978
Returns: string
8079
8180
"""
82-
str_len = ClientThread.read_int32(conn)
81+
str_len = ClientThread.read_int32(self.conn)
8382

84-
str_bytes = ClientThread.recvall(conn, str_len)
85-
decoded_str = str_bytes.decode("utf-8")
83+
str_bytes = ClientThread.recvall(self.conn, str_len)
84+
decoded_str = str_bytes.decode('utf-8')
8685

8786
return decoded_str
8887

89-
@staticmethod
90-
def read_message(conn):
88+
def read_message(self):
9189
"""
9290
Decode destination and full message size from socket connection.
9391
Grab bytes in chunks until full message has been read.
9492
"""
95-
data = b""
93+
data = b''
9694

97-
destination = ClientThread.read_string(conn)
98-
full_message_size = ClientThread.read_int32(conn)
95+
destination = self.read_string()
96+
full_message_size = ClientThread.read_int32(self.conn)
9997

10098
while len(data) < full_message_size:
10199
# Only grabs max of 1024 bytes TODO: change to TCPServer's buffer_size
102100
grab = 1024 if full_message_size - len(data) > 1024 else full_message_size - len(data)
103-
packet = ClientThread.recvall(conn, grab)
101+
packet = ClientThread.recvall(self.conn, grab)
104102

105103
if not packet:
106-
rospy.logerr("No packets...")
104+
self.logerr("No packets...")
107105
break
108106

109107
data += packet
110108

111109
if full_message_size > 0 and not data:
112-
rospy.logerr("No data for a message size of {}, breaking!".format(full_message_size))
110+
self.logerr("No data for a message size of {}, breaking!".format(full_message_size))
113111
return
114112

113+
destination = destination.rstrip('\x00')
115114
return destination, data
116115

117116
@staticmethod
@@ -126,22 +125,14 @@ def serialize_message(destination, message):
126125
Returns:
127126
serialized destination and message as a list of bytes
128127
"""
129-
dest_bytes = destination.encode("utf-8")
128+
dest_bytes = destination.encode('utf-8')
130129
length = len(dest_bytes)
131-
dest_info = struct.pack("<I%ss" % length, length, dest_bytes)
130+
dest_info = struct.pack('<I%ss' % length, length, dest_bytes)
132131

133-
serial_response = BytesIO()
134-
message.serialize(serial_response)
132+
serial_response = serialize_message(message)
135133

136-
# Per documention, https://docs.python.org/3.8/library/io.html#io.IOBase.seek,
137-
# seek to end of stream for length
138-
# SEEK_SET or 0 - start of the stream (the default); offset should be zero or positive
139-
# SEEK_CUR or 1 - current stream position; offset may be negative
140-
# SEEK_END or 2 - end of the stream; offset is usually negative
141-
response_len = serial_response.seek(0, 2)
142-
143-
msg_length = struct.pack("<I", response_len)
144-
serialized_message = dest_info + msg_length + serial_response.getvalue()
134+
msg_length = struct.pack('<I', len(serial_response))
135+
serialized_message = dest_info + msg_length + serial_response
145136

146137
return serialized_message
147138

@@ -156,14 +147,14 @@ def serialize_command(command, params):
156147
json_info = struct.pack("<I%ss" % json_length, json_length, json_bytes)
157148

158149
return cmd_info + json_info
159-
150+
160151
def send_ros_service_request(self, srv_id, destination, data):
161152
if destination not in self.tcp_server.source_destination_dict.keys():
162153
error_msg = "Service destination '{}' is not registered! Known topics are: {} ".format(
163154
destination, self.tcp_server.source_destination_dict.keys()
164155
)
165156
self.tcp_server.send_unity_error(error_msg)
166-
rospy.logerr(error_msg)
157+
self.logerr(error_msg)
167158
# TODO: send a response to Unity anyway?
168159
return
169160
else:
@@ -176,12 +167,18 @@ def send_ros_service_request(self, srv_id, destination, data):
176167
destination
177168
)
178169
self.tcp_server.send_unity_error(error_msg)
179-
rospy.logerr(error_msg)
170+
self.logerr(error_msg)
180171
# TODO: send a response to Unity anyway?
181172
return
182173

183174
self.tcp_server.unity_tcp_sender.send_ros_service_response(srv_id, destination, response)
184175

176+
def loginfo(self, text):
177+
self.tcp_server.get_logger().info(text)
178+
179+
def logerr(self, text):
180+
self.tcp_server.get_logger().error(text)
181+
185182
def run(self):
186183
"""
187184
Read a message and determine where to send it based on the source_destination_dict
@@ -197,13 +194,13 @@ def run(self):
197194
msg: the ROS msg type as bytes
198195
199196
"""
200-
rospy.loginfo("Connection from {}".format(self.incoming_ip))
197+
self.loginfo("Connection from {}".format(self.incoming_ip))
201198
halt_event = threading.Event()
202199
self.tcp_server.unity_tcp_sender.start_sender(self.conn, halt_event)
203200
try:
204201
while not halt_event.is_set():
205-
destination, data = self.read_message(self.conn)
206-
202+
destination, data = self.read_message()
203+
207204
if self.tcp_server.pending_srv_id is not None:
208205
# if we've been told that the next message will be a service request/response, process it as such
209206
if self.tcp_server.pending_srv_is_request:
@@ -212,23 +209,22 @@ def run(self):
212209
self.tcp_server.send_unity_service_response(self.tcp_server.pending_srv_id, data)
213210
self.tcp_server.pending_srv_id = None
214211
elif destination == "":
215-
# ignore this keepalive message, listen for more
212+
#ignore this keepalive message, listen for more
216213
pass
217214
elif destination.startswith("__"):
218-
# handle a system command, such as registering new topics
215+
#handle a system command, such as registering new topics
219216
self.tcp_server.handle_syscommand(destination, data)
220217
elif destination in self.tcp_server.source_destination_dict:
221218
ros_communicator = self.tcp_server.source_destination_dict[destination]
222219
response = ros_communicator.send(data)
223220
else:
224-
error_msg = "Topic '{}' is not registered! Known topics are: {} ".format(
225-
destination, self.tcp_server.source_destination_dict.keys()
226-
)
221+
error_msg = "Topic '{}' is not registered! Known topics are: {} "\
222+
.format(destination, self.tcp_server.source_destination_dict.keys())
227223
self.tcp_server.send_unity_error(error_msg)
228-
rospy.logerr(error_msg)
224+
self.logerr(error_msg)
229225
except IOError as e:
230-
rospy.logerr("Exception: {}".format(e))
226+
self.logerr("Exception: {}".format(e))
231227
finally:
232228
halt_event.set()
233229
self.conn.close()
234-
rospy.loginfo("Disconnected from {}".format(self.incoming_ip))
230+
self.loginfo("Disconnected from {}".format(self.incoming_ip));

src/ros_tcp_endpoint/communication.py renamed to ros_tcp_endpoint/communication.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,28 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import rclpy
16+
from rclpy.node import Node
1517

16-
class RosSender:
18+
19+
class RosSender(Node):
1720
"""
18-
Base class for ROS communication where data is sent to the ROS network.
21+
Base class for ROS communication where data is sent to the ROS network.
1922
"""
20-
21-
def __init__(self):
23+
def __init__(self, node_name):
24+
super().__init__(node_name)
2225
pass
2326

2427
def send(self, *args):
2528
raise NotImplementedError
2629

2730

28-
class RosReceiver:
31+
class RosReceiver(Node):
2932
"""
30-
Base class for ROS communication where data is being sent outside of the ROS network.
33+
Base class for ROS communication where data is being sent outside of the ROS network.
3134
"""
32-
33-
def __init__(self):
35+
def __init__(self, node_name):
36+
super().__init__(node_name)
3437
pass
3538

3639
def send(self, *args):
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python
2+
3+
import rclpy
4+
5+
from ros2_tcp_endpoint import TcpServer
6+
7+
8+
def main(args=None):
9+
rclpy.init(args=args)
10+
tcp_server = TcpServer('TCPServer')
11+
12+
tcp_server.start()
13+
14+
tcp_server.setup_executor()
15+
16+
tcp_server.destroy_nodes()
17+
rclpy.shutdown()
18+
19+
20+
if __name__ == "__main__":
21+
main()

0 commit comments

Comments
 (0)