Skip to content

Commit 54c1a64

Browse files
authored
Release 0.7.0 (ROS2)
2 parents 09d97b2 + d43d095 commit 54c1a64

20 files changed

+196
-121
lines changed

.github/workflows/jira-link.yaml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: jira-link
2+
3+
on:
4+
pull_request:
5+
types: [opened, edited, reopened, synchronize]
6+
7+
jobs:
8+
jira-link:
9+
runs-on: ubuntu-20.04
10+
steps:
11+
- name: check pull request title and source branch name
12+
run: |
13+
echo "Checking pull request with title ${{ github.event.pull_request.title }} from source branch ${{ github.event.pull_request.head.ref }}"
14+
if ! [[ "${{ github.event.pull_request.title }}" =~ ^AIRO-[0-9]+[[:space:]].*$ ]] && ! [[ "${{ github.event.pull_request.head.ref }}" =~ ^AIRO-[0-9]+.*$ ]]
15+
then
16+
echo -e "Please make sure one of the following is true:\n \
17+
1. the pull request title starts with 'AIRO-xxxx ', e.g. 'AIRO-1024 My Pull Request'\n \
18+
2. the source branch starts with 'AIRO-xxx', e.g. 'AIRO-1024-my-branch'"
19+
exit 1
20+
else
21+
echo "Completed checking"
22+
fi

.github/workflows/pre-commit.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ jobs:
1010
- uses: actions/checkout@v2
1111
- uses: actions/setup-python@v2
1212
with:
13-
python-version: 3.7.x
13+
python-version: 3.7.x
14+

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
.DS_Store
22
*.pyc
33
.idea
4+
.coverage
5+
test-results/
46
*~
57
build
68
devel

.yamato/sonar.yml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Sonarqube Standard Scan
2+
agent:
3+
type: Unity::metal::macmini
4+
image: package-ci/mac
5+
flavor: m1.mac
6+
variables:
7+
SONARQUBE_PROJECT_KEY: ai-robotics-endpoint-ros2
8+
commands:
9+
- curl https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472-macosx.zip -o sonar-scanner-macosx.zip -L
10+
- unzip sonar-scanner-macosx.zip -d ~/sonar-scanner
11+
- ~/sonar-scanner/sonar-scanner-4.6.2.2472-macosx/bin/sonar-scanner -Dsonar.projectKey=$SONARQUBE_PROJECT_KEY -Dsonar.sources=. -Dsonar.host.url=$SONARQUBE_ENDPOINT_URL_PRD -Dsonar.login=$SONARQUBE_TOKEN_PRD
12+
triggers:
13+
cancel_old_ci: true
14+
expression: |
15+
((pull_request.target eq "main" OR pull_request.target eq "dev-ros2")
16+
AND NOT pull_request.push.changes.all match "**/*.md") OR
17+
(push.branch eq "main" OR push.branch eq "dev-ros2")

.yamato/yamato-config.yml

+14-10
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
name: Endpoint Unit Tests
22
agent:
33
type: Unity::VM
4-
image: robotics/ci-ubuntu20:v0.1.0-795910
4+
image: robotics/ci-ros2-galactic-ubuntu20:v0.0.2-916903
55
flavor: i1.large
6+
variables:
7+
# Coverage measured as a percentage (out of 100)
8+
COVERAGE_EXPECTED: 3.5
69
commands:
710
# run unit tests and save test results in /home/bokken/build/output/Unity-Technologies/ROS-TCP-Endpoint
811
- command: |
9-
source /opt/ros/noetic/setup.bash && echo "ROS_DISTRO == $ROS_DISTRO"
10-
cd .. && mkdir -p catkin_ws/src && cp -r ./ROS-TCP-Endpoint catkin_ws/src
11-
cd catkin_ws && catkin_make && source devel/setup.bash
12+
source /opt/ros/galactic/setup.bash && echo "ROS_DISTRO == galactic"
13+
cd .. && mkdir -p ros_ws/src && cp -r ./ROS-TCP-Endpoint ros_ws/src
14+
cd ros_ws && colcon build && source install/local_setup.bash
1215
cd src/ROS-TCP-Endpoint
13-
python3 -m pytest --cov=. --cov-report xml:../../../ROS-TCP-Endpoint/test-results/coverage.xml --cov-report html:../../../ROS-TCP-Endpoint/test-results/coverage.html test/
16+
python3 -m pytest --cov=. --cov-report xml:./test-results/coverage.xml --cov-report html:./test-results/coverage.html test/
1417
# check the test coverage
1518
- command: |
1619
linecoverage=$(head -2 test-results/coverage.xml | grep -Eo 'line-rate="[0-9]+([.][0-9]+)?"' | grep -Eo "[0-9]+([.][0-9]+)?")
1720
echo "Line coverage: $linecoverage"
18-
if (( $(echo "$linecoverage < 0.3" | bc -l) )); then exit 1; fi
21+
if (( $(echo "$linecoverage * 100.0 < $COVERAGE_EXPECTED" | bc -l) ));
22+
then echo "ERROR: Code Coverage is under threshold of $COVERAGE_EXPECTED%" && exit 1
23+
fi
1924
triggers:
2025
cancel_old_ci: true
2126
expression: |
22-
(pull_request.target eq "main" AND
23-
NOT pull_request.push.changes.all match "**/*.md") OR
24-
(pull_request.target eq "dev" AND
25-
NOT pull_request.push.changes.all match "**/*.md")
27+
((pull_request.target eq "main-ros2" OR pull_request.target eq "dev-ros2")
28+
AND NOT pull_request.push.changes.all match "**/*.md") OR
29+
(push.branch eq "main-ros2" OR push.branch eq "dev-ros2")
2630
artifacts:
2731
logs:
2832
paths:

CHANGELOG.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
1212

1313
### Added
1414

15+
Added Sonarqube scanner
16+
1517
### Changed
1618

1719
### Deprecated
@@ -20,6 +22,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
2022

2123
### Fixed
2224

25+
26+
## [0.7.0] - 2022-02-01
27+
28+
### Added
29+
30+
Added Sonarqube scanner
31+
32+
Send information during hand shaking for ros and package version checks
33+
34+
Send service response as one queue item
35+
36+
2337
## [0.6.0] - 2021-09-30
2438

2539
Add the [Close Stale Issues](https://github.com/marketplace/actions/close-stale-issues) action
@@ -84,4 +98,4 @@ Improving the performance of the read_message in client.py, This is done by rece
8498

8599
Remove outdated handshake references
86100

87-
### Fixed
101+
### Fixed

launch/endpoint.py

+10-12
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33

44

55
def generate_launch_description():
6-
return LaunchDescription([
7-
Node(
8-
package='ros_tcp_endpoint',
9-
executable='default_server_endpoint',
10-
emulate_tty=True,
11-
parameters=[
12-
{'ROS_IP': '0.0.0.0'},
13-
{'ROS_TCP_PORT': 10000},
14-
],
15-
),
16-
])
17-
6+
return LaunchDescription(
7+
[
8+
Node(
9+
package="ros_tcp_endpoint",
10+
executable="default_server_endpoint",
11+
emulate_tty=True,
12+
parameters=[{"ROS_IP": "0.0.0.0"}, {"ROS_TCP_PORT": 10000}],
13+
)
14+
]
15+
)

package.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
33
<package format="3">
44
<name>ros_tcp_endpoint</name>
5-
<version>0.6.0</version>
5+
<version>0.7.0</version>
66
<description>ROS TCP Endpoint Unity Integration (ROS2 version)
77
Acts as the bridge between Unity messages sent via TCP socket and ROS messages.
88
</description>

ros_tcp_endpoint/client.py

+6-12
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def send_ros_service_request(self, srv_id, destination, data):
148148
destination, self.tcp_server.ros_services_table.keys()
149149
)
150150
self.tcp_server.send_unity_error(error_msg)
151-
self.logerr(error_msg)
151+
self.tcp_server.logerr(error_msg)
152152
# TODO: send a response to Unity anyway?
153153
return
154154
else:
@@ -165,18 +165,12 @@ def service_call_thread(self, srv_id, destination, data, ros_communicator):
165165
if not response:
166166
error_msg = "No response data from service '{}'!".format(destination)
167167
self.tcp_server.send_unity_error(error_msg)
168-
self.logerr(error_msg)
168+
self.tcp_server.logerr(error_msg)
169169
# TODO: send a response to Unity anyway?
170170
return
171171

172172
self.tcp_server.unity_tcp_sender.send_ros_service_response(srv_id, destination, response)
173173

174-
def loginfo(self, text):
175-
self.tcp_server.get_logger().info(text)
176-
177-
def logerr(self, text):
178-
self.tcp_server.get_logger().error(text)
179-
180174
def run(self):
181175
"""
182176
Receive a message from Unity and determine where to send it based on the publishers table
@@ -192,7 +186,7 @@ def run(self):
192186
msg: the ROS msg type as bytes
193187
194188
"""
195-
self.loginfo("Connection from {}".format(self.incoming_ip))
189+
self.tcp_server.loginfo("Connection from {}".format(self.incoming_ip))
196190
halt_event = threading.Event()
197191
self.tcp_server.unity_tcp_sender.start_sender(self.conn, halt_event)
198192
try:
@@ -225,10 +219,10 @@ def run(self):
225219
destination, self.tcp_server.publishers_table.keys()
226220
)
227221
self.tcp_server.send_unity_error(error_msg)
228-
self.logerr(error_msg)
222+
self.tcp_server.logerr(error_msg)
229223
except IOError as e:
230-
self.logerr("Exception: {}".format(e))
224+
self.tcp_server.logerr("Exception: {}".format(e))
231225
finally:
232226
halt_event.set()
233227
self.conn.close()
234-
self.loginfo("Disconnected from {}".format(self.incoming_ip))
228+
self.tcp_server.loginfo("Disconnected from {}".format(self.incoming_ip))

ros_tcp_endpoint/communication.py

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class RosSender(Node):
2020
"""
2121
Base class for ROS communication where data is sent to the ROS network.
2222
"""
23+
2324
def __init__(self, node_name):
2425
super().__init__(node_name)
2526
pass
@@ -32,6 +33,7 @@ class RosReceiver(Node):
3233
"""
3334
Base class for ROS communication where data is being sent outside of the ROS network.
3435
"""
36+
3537
def __init__(self, node_name):
3638
super().__init__(node_name)
3739
pass

ros_tcp_endpoint/default_server_endpoint.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
def main(args=None):
99
rclpy.init(args=args)
10-
tcp_server = TcpServer('TCPServer')
10+
tcp_server = TcpServer("UnityEndpoint")
1111

1212
tcp_server.start()
1313

ros_tcp_endpoint/server.py

+28-25
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,13 @@ def __init__(self, node_name, buffer_size=1024, connections=10, tcp_ip=None, tcp
5252
self.declare_parameter("ROS_TCP_PORT", 10000)
5353

5454
if tcp_ip:
55-
self.get_logger().log("Using ROS_IP override from constructor: {}".format(tcp_ip))
55+
self.loginfo("Using ROS_IP override from constructor: {}".format(tcp_ip))
5656
self.tcp_ip = tcp_ip
5757
else:
5858
self.tcp_ip = self.get_parameter("ROS_IP").get_parameter_value().string_value
5959

6060
if tcp_port:
61-
self.get_logger().log(
62-
"Using ROS_TCP_PORT override from constructor: {}".format(tcp_port)
63-
)
61+
self.loginfo("Using ROS_TCP_PORT override from constructor: {}".format(tcp_port))
6462
self.tcp_port = tcp_port
6563
else:
6664
self.tcp_port = self.get_parameter("ROS_TCP_PORT").get_parameter_value().integer_value
@@ -93,7 +91,7 @@ def listen_loop(self):
9391
Creates and binds sockets using TCP variables then listens for incoming connections.
9492
For each new connection a client thread will be created to handle communication.
9593
"""
96-
self.get_logger().info("Starting server on {}:{}".format(self.tcp_ip, self.tcp_port))
94+
self.loginfo("Starting server on {}:{}".format(self.tcp_ip, self.tcp_port))
9795
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9896
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
9997
tcp_server.bind((self.tcp_ip, self.tcp_port))
@@ -105,7 +103,7 @@ def listen_loop(self):
105103
(conn, (ip, port)) = tcp_server.accept()
106104
ClientThread(conn, self, ip, port).start()
107105
except socket.timeout as err:
108-
self.get_logger().error("ros_tcp_endpoint.TcpServer: socket timeout")
106+
self.logerr("ros_tcp_endpoint.TcpServer: socket timeout")
109107

110108
def send_unity_error(self, error):
111109
self.unity_tcp_sender.send_unity_error(error)
@@ -128,6 +126,15 @@ def handle_syscommand(self, topic, data):
128126
params = json.loads(message_json)
129127
function(**params)
130128

129+
def loginfo(self, text):
130+
self.get_logger().info(text)
131+
132+
def logwarn(self, text):
133+
self.get_logger().warning(text)
134+
135+
def logerr(self, text):
136+
self.get_logger().error(text)
137+
131138
def setup_executor(self):
132139
"""
133140
Since rclpy.spin() is a blocking call the server needed a way
@@ -136,7 +143,13 @@ def setup_executor(self):
136143
MultiThreadedExecutor allows us to set the number of threads
137144
needed as well as the nodes that need to be spun.
138145
"""
139-
num_threads = len(self.publishers_table.keys()) + len(self.subscribers_table.keys()) + len(self.ros_services_table.keys()) + len(self.unity_services_table.keys()) + 1
146+
num_threads = (
147+
len(self.publishers_table.keys())
148+
+ len(self.subscribers_table.keys())
149+
+ len(self.ros_services_table.keys())
150+
+ len(self.unity_services_table.keys())
151+
+ 1
152+
)
140153
executor = MultiThreadedExecutor(num_threads)
141154

142155
executor.add_node(self)
@@ -204,9 +217,7 @@ def subscribe(self, topic, message_name):
204217
if self.tcp_server.executor is not None:
205218
self.tcp_server.executor.add_node(new_subscriber)
206219

207-
self.tcp_server.get_logger().info(
208-
"RegisterSubscriber({}, {}) OK".format(topic, message_class)
209-
)
220+
self.tcp_server.loginfo("RegisterSubscriber({}, {}) OK".format(topic, message_class))
210221

211222
def publish(self, topic, message_name, queue_size=10, latch=False):
212223
if topic == "":
@@ -234,9 +245,7 @@ def publish(self, topic, message_name, queue_size=10, latch=False):
234245
if self.tcp_server.executor is not None:
235246
self.tcp_server.executor.add_node(new_publisher)
236247

237-
self.tcp_server.get_logger().info(
238-
"RegisterPublisher({}, {}) OK".format(topic, message_class)
239-
)
248+
self.tcp_server.loginfo("RegisterPublisher({}, {}) OK".format(topic, message_class))
240249

241250
def ros_service(self, topic, message_name):
242251
if topic == "":
@@ -265,9 +274,7 @@ def ros_service(self, topic, message_name):
265274
if self.tcp_server.executor is not None:
266275
self.tcp_server.executor.add_node(new_service)
267276

268-
self.tcp_server.get_logger().info(
269-
"RegisterRosService({}, {}) OK".format(topic, message_class)
270-
)
277+
self.tcp_server.loginfo("RegisterRosService({}, {}) OK".format(topic, message_class))
271278

272279
def unity_service(self, topic, message_name):
273280
if topic == "":
@@ -297,9 +304,7 @@ def unity_service(self, topic, message_name):
297304
if self.tcp_server.executor is not None:
298305
self.tcp_server.executor.add_node(new_service)
299306

300-
self.tcp_server.get_logger().info(
301-
"RegisterUnityService({}, {}) OK".format(topic, message_class)
302-
)
307+
self.tcp_server.loginfo("RegisterUnityService({}, {}) OK".format(topic, message_class))
303308

304309
def response(self, srv_id): # the next message is a service response
305310
self.tcp_server.pending_srv_id = srv_id
@@ -320,20 +325,18 @@ def resolve_message_name(self, name, extension="msg"):
320325
importlib.import_module(module_name + "." + extension)
321326
module = sys.modules[module_name]
322327
if module is None:
323-
self.tcp_server.get_logger().error(
324-
"Failed to resolve module {}".format(module_name)
325-
)
328+
self.tcp_server.logerr("Failed to resolve module {}".format(module_name))
326329
module = getattr(module, extension)
327330
if module is None:
328-
self.tcp_server.get_logger().error(
331+
self.tcp_server.logerr(
329332
"Failed to resolve module {}.{}".format(module_name, extension)
330333
)
331334
module = getattr(module, class_name)
332335
if module is None:
333-
self.tcp_server.get_logger().error(
336+
self.tcp_server.logerr(
334337
"Failed to resolve module {}.{}.{}".format(module_name, extension, class_name)
335338
)
336339
return module
337340
except (IndexError, KeyError, AttributeError, ImportError) as e:
338-
self.tcp_server.get_logger().error("Failed to resolve message name: {}".format(e))
341+
self.tcp_server.logerr("Failed to resolve message name: {}".format(e))
339342
return None

0 commit comments

Comments
 (0)