Skip to content

Commit a446013

Browse files
authored
Merge pull request #3 from FunPythonEC/bug-topic-negotiation
Robust bug topic negotiation
2 parents 5bed97a + 255b0b5 commit a446013

File tree

2 files changed

+80
-66
lines changed

2 files changed

+80
-66
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,4 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
.vscode

src/uros/core.py

Lines changed: 79 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,14 @@
44
uio: packet buffer
55
struct: serlization
66
_TopicInfo: for topic negotiation
7-
already provided in rosserial_msgs
7+
already provided in rosserial_msgs
88
"""
99

10+
import sys
11+
import logging
1012
import machine as m
1113
import uio
12-
import ustruct as struct
13-
from time import sleep, sleep_ms, sleep_us
1414
from rosserial_msgs import TopicInfo
15-
import sys
16-
import os
17-
import logging
1815

1916
# for now threads are used, will be changed with asyncio in the future
2017
if sys.platform == "esp32":
@@ -29,17 +26,18 @@
2926

3027
# class to manage publish and subscribe
3128
# COULD BE CHANGED AFTERWARDS
32-
class NodeHandle(object):
33-
def __init__(self, serial_id=2, baudrate=115200, **kwargs):
29+
class NodeHandle:
30+
"""Initiates connection through rosserial using UART ports."""
3431

32+
def __init__(self, serial_id=2, baudrate=115200, **kwargs):
3533
"""
36-
id: used for topics id (negotiation)
37-
advertised_topics: manage already negotiated topics
38-
subscribing_topics: topics to which will be subscribed are here
39-
serial_id: uart id
40-
baudrate: baudrate used for serial comm
41-
"""
42-
self.id = 101
34+
id: used for topics id (negotiation)
35+
advertised_topics: manage already negotiated topics
36+
subscribing_topics: topics to which will be subscribed are here
37+
serial_id: uart id
38+
baudrate: baudrate used for serial comm
39+
"""
40+
self._id = 101
4341
self.advertised_topics = dict()
4442
self.subscribing_topics = dict()
4543
self.serial_id = serial_id
@@ -69,28 +67,18 @@ def __init__(self, serial_id=2, baudrate=115200, **kwargs):
6967

7068
# method to manage and advertise topic
7169
# before publishing or subscribing
72-
def _advertise_topic(self, topic_name, msg, endpoint, buffer_size):
73-
70+
def _advertise_topic(self, topic_name, _id, msg, endpoint, buffer_size):
71+
"""
72+
topic_name: eg. (Greet)
73+
msg: message object
74+
endpoint: corresponds to TopicInfo.msg typical topic id values
7475
"""
75-
topic_name: eg. (Greet)
76-
msg: message object
77-
endpoint: corresponds to TopicInfo.msg typical topic id values
78-
"""
7976
register = TopicInfo()
80-
register.topic_id = self.id
77+
register.topic_id = _id
8178
register.topic_name = topic_name
8279
register.message_type = msg._type
8380
register.md5sum = msg._md5sum
84-
85-
self.advertised_topics[topic_name] = self.id
86-
87-
# id are summed by one
88-
self.id += 1
89-
90-
try:
91-
register.buffer_size = buffer_size
92-
except Exception as e:
93-
logging.info("No buffer size could be defined for topic negotiation.")
81+
register.buffer_size = buffer_size
9482

9583
# serialization
9684
packet = uio.StringIO()
@@ -101,17 +89,31 @@ def _advertise_topic(self, topic_name, msg, endpoint, buffer_size):
10189
length = len(packet)
10290

10391
# both checksums
104-
crclen = [checksum(le(length))]
105-
crcpack = [checksum(le(endpoint) + packet)]
92+
crclen = [checksum(_le(length))]
93+
crcpack = [checksum(_le(endpoint) + packet)]
10694

10795
# final packet to be sent
108-
fpacket = header + le(length) + crclen + le(endpoint) + packet + crcpack
96+
fpacket = header + _le(length) + crclen + _le(endpoint) + packet + crcpack
10997
self.uart.write(bytearray(fpacket))
11098

99+
def _advertise_all_topics(self):
100+
for key, value in self.advertised_topics.items():
101+
self._advertise_topic(key, value[0], value[1], value[2], value[3])
102+
111103
def publish(self, topic_name, msg, buffer_size=1024):
104+
"""publishes messages to topics
105+
106+
Args:
107+
topic_name (string): name of destination topic in ROS network.
108+
msg (ROS message): custom message object generated by ugenpy.
109+
buffer_size (int, optional): maximum size of buffer for message. Defaults to 1024.
110+
"""
112111

113112
if topic_name not in self.advertised_topics:
114-
self._advertise_topic(topic_name, msg, 0, buffer_size)
113+
self.advertised_topics[topic_name] = [self._id, msg, 0, buffer_size]
114+
# id are summed by one
115+
self._advertise_topic(topic_name, self._id, msg, 0, buffer_size)
116+
self._id += 1
115117

116118
# same as advertise
117119
packet = uio.StringIO()
@@ -120,23 +122,33 @@ def publish(self, topic_name, msg, buffer_size=1024):
120122
packet = list(packet.getvalue().encode("utf-8"))
121123
length = len(packet)
122124

123-
topic_id = le(self.advertised_topics.get(topic_name))
124-
crclen = [checksum(le(length))]
125+
topic_id = _le(self.advertised_topics.get(topic_name)[0])
126+
crclen = [checksum(_le(length))]
125127
crcpack = [checksum(topic_id + packet)]
126128

127-
fpacket = header + le(length) + crclen + topic_id + packet + crcpack
129+
fpacket = header + _le(length) + crclen + topic_id + packet + crcpack
128130
self.uart.write(bytearray(fpacket))
129131

130-
def subscribe(self, topic_name, msgobj, cb, buffer_size=1024):
131-
assert cb is not None, "Subscribe callback is not set"
132+
def subscribe(self, topic_name, msg, _cb, buffer_size=1024):
133+
"""subscribes to a topic receiving messages and processing them by a callback function
134+
135+
Args:
136+
topic_name (string): name of destiny topic to send messages.
137+
msg (ROS message): custom message object generated by ugenpy.
138+
cb (function): callback function to process incoming messages.
139+
buffer_size (int, optional): maximum size of buffer for message. Defaults to 1024.
140+
"""
141+
assert _cb is not None, "Subscribe callback is not set"
132142

133143
# subscribing topic attributes are added
134-
self.subscribing_topics[self.id] = [msgobj, cb]
144+
self.subscribing_topics[self._id] = [msg, _cb]
135145

136146
# advertised if not already subscribed
137147
if topic_name not in self.advertised_topics:
138-
msg = msgobj()
139-
self._advertise_topic(topic_name, msg, 1, buffer_size)
148+
self.advertised_topics[topic_name] = [self._id, msg, 1, buffer_size]
149+
# id are summed by one
150+
self._advertise_topic(topic_name, self._id, msg, 1, buffer_size)
151+
self._id += 1
140152

141153
def _listen(self):
142154
while True:
@@ -166,46 +178,47 @@ def _listen(self):
166178
try:
167179
# incoming object msg initialized
168180
msgobj = self.subscribing_topics.get(inid)[0]
169-
except Exception:
170-
logging.info("TX request was made or got message from not available subscribed topic.")
181+
except (OSError, TypeError, IndexError):
182+
logging.info(
183+
"TX request was made or got message from"
184+
+ "not available subscribed topic."
185+
)
171186
# object sent to callback
172187
callback = self.subscribing_topics.get(inid)[1]
173188
fdata = msgobj()
174189
fdata = fdata.deserialize(msgdata)
175190
callback(fdata)
176191
else:
177192
raise ValueError("Message plus Topic ID Checksum is wrong!")
193+
else:
194+
self._advertise_all_topics()
178195

179-
except Exception as e:
196+
except (OSError, TypeError, ValueError):
180197
logging.info("No incoming data could be read for subscribes.")
181198

182199

183200
# functions to be used in class
184-
def word(l, h):
201+
def word(_l, _h):
202+
"""
203+
Given a low and high bit, converts the number back into a word.
185204
"""
186-
Given a low and high bit, converts the number back into a word.
187-
"""
188-
return (h << 8) + l
205+
return (_h << 8) + _l
189206

190207

191208
# checksum method, receives array
192209
def checksum(arr):
193-
return 255 - ((sum(arr)) % 256)
194-
210+
"""Generates checksum value of message.
195211
196-
# little-endian method
197-
def le(h):
198-
h &= 0xFFFF
199-
return [h & 0xFF, h >> 8]
212+
Args:
213+
arr (list): list of hexadecimal values.
200214
215+
Returns:
216+
[int]: returns an value between 0 and 256
217+
"""
218+
return 255 - ((sum(arr)) % 256)
201219

202-
# example code
203-
if __name__ == "__main__":
204-
from std_msgs import String
205-
from uros import NodeHandle
206220

207-
msg = String()
208-
msg.data = "HiItsMeMario"
209-
node = NodeHandle(2, 115200)
210-
while True:
211-
node.publish("greet", msg)
221+
# little-endian method
222+
def _le(_h):
223+
_h &= 0xFFFF
224+
return [_h & 0xFF, _h >> 8]

0 commit comments

Comments
 (0)