|
14 | 14 | from rosserial_msgs import TopicInfo
|
15 | 15 | import sys
|
16 | 16 | import os
|
17 |
| -#for now threads are used, will be changed with asyncio in the future |
| 17 | + |
| 18 | +# for now threads are used, will be changed with asyncio in the future |
18 | 19 | if sys.platform == "esp32":
|
19 | 20 | import _thread as threading
|
20 | 21 | else:
|
21 | 22 | import threading
|
22 | 23 |
|
23 |
| -#rosserial protocol header |
24 |
| -header=[0xff,0xfe] |
| 24 | +# rosserial protocol header |
| 25 | +header = [0xFF, 0xFE] |
25 | 26 |
|
26 |
| -#class to manage publish and subscribe |
27 |
| -#COULD BE CHANGED AFTERWARDS |
| 27 | +# class to manage publish and subscribe |
| 28 | +# COULD BE CHANGED AFTERWARDS |
28 | 29 | class NodeHandle(object):
|
29 |
| - def __init__(self, serial_id, baudrate): |
30 |
| - |
31 |
| - """ |
| 30 | + def __init__(self, serial_id, baudrate): |
| 31 | + |
| 32 | + """ |
32 | 33 | id: used for topics id (negotiation)
|
33 | 34 | advertised_topics: manage already negotiated topics
|
34 | 35 | subscribing_topics: topics to which will be subscribed are here
|
35 | 36 | serial_id: uart id
|
36 | 37 | baudrate: baudrate used for serial comm
|
37 | 38 | """
|
38 |
| - self.id=101 |
39 |
| - self.advertised_topics=dict() |
40 |
| - self.subscribing_topics=dict() |
41 |
| - self.serial_id=serial_id |
42 |
| - self.baudrate=baudrate |
43 |
| - self.uart = m.UART(self.serial_id, self.baudrate) |
44 |
| - self.uart.init(self.baudrate, bits=8, parity=None, stop=1, txbuf=0) |
45 |
| - |
46 |
| - if sys.platform == "esp32": |
47 |
| - threading.start_new_thread(self._listen, ()) |
48 |
| - else: |
49 |
| - threading.Thread(target = self._listen).start() |
50 |
| - |
51 |
| - |
52 |
| - #method to manage and advertise topic |
53 |
| - #before publishing or subscribing |
54 |
| - def _advertise_topic(self, topic_name, msg, endpoint, buffer_size): |
55 |
| - |
56 |
| - """ |
| 39 | + self.id = 101 |
| 40 | + self.advertised_topics = dict() |
| 41 | + self.subscribing_topics = dict() |
| 42 | + self.serial_id = serial_id |
| 43 | + self.baudrate = baudrate |
| 44 | + self.uart = m.UART(self.serial_id, self.baudrate) |
| 45 | + self.uart.init(self.baudrate, bits=8, parity=None, stop=1, txbuf=0) |
| 46 | + |
| 47 | + if sys.platform == "esp32": |
| 48 | + threading.start_new_thread(self._listen, ()) |
| 49 | + else: |
| 50 | + threading.Thread(target=self._listen).start() |
| 51 | + |
| 52 | + # method to manage and advertise topic |
| 53 | + # before publishing or subscribing |
| 54 | + def _advertise_topic(self, topic_name, msg, endpoint, buffer_size): |
| 55 | + |
| 56 | + """ |
57 | 57 | topic_name: eg. (Greet)
|
58 | 58 | msg: message object
|
59 | 59 | endpoint: corresponds to TopicInfo.msg typical topic id values
|
60 | 60 | """
|
61 |
| - register=TopicInfo() |
62 |
| - register.topic_id=self.id |
63 |
| - register.topic_name=topic_name |
64 |
| - register.message_type=msg._type |
65 |
| - register.md5sum=msg._md5sum |
66 |
| - |
67 |
| - self.advertised_topics[topic_name]=self.id |
68 |
| - |
69 |
| - #id are summed by one |
70 |
| - self.id+=1 |
71 |
| - |
72 |
| - try: |
73 |
| - register.buffer_size=buffer_size |
74 |
| - except Exception as e: |
75 |
| - print('No buffer size could be defined for topic negotiation.') |
76 |
| - |
77 |
| - #serialization |
78 |
| - packet=uio.StringIO() |
79 |
| - register.serialize(packet) |
80 |
| - |
81 |
| - #already serialized (packet) |
82 |
| - packet=list(packet.getvalue().encode('utf-8')) |
83 |
| - length=len(packet) |
84 |
| - |
85 |
| - #both checksums |
86 |
| - crclen=[checksum(le(length))] |
87 |
| - crcpack=[checksum(le(endpoint)+packet)] |
88 |
| - |
89 |
| - #final packet to be sent |
90 |
| - fpacket=header+le(length)+crclen+le(endpoint)+packet+crcpack |
91 |
| - self.uart.write(bytearray(fpacket)) |
92 |
| - |
93 |
| - |
94 |
| - def publish(self, topic_name, msg, buffer_size=1024): |
95 |
| - |
96 |
| - if topic_name not in self.advertised_topics: |
97 |
| - self._advertise_topic(topic_name, msg, 0, buffer_size) |
98 |
| - |
99 |
| - #same as advertise |
100 |
| - packet=uio.StringIO() |
101 |
| - msg.serialize(packet) |
102 |
| - |
103 |
| - packet=list(packet.getvalue().encode('utf-8')) |
104 |
| - length=len(packet) |
105 |
| - |
106 |
| - topic_id=le(self.advertised_topics.get(topic_name)) |
107 |
| - crclen=[checksum(le(length))] |
108 |
| - crcpack=[checksum(topic_id+packet)] |
109 |
| - |
110 |
| - fpacket=header+le(length)+crclen+topic_id+packet+crcpack |
111 |
| - self.uart.write(bytearray(fpacket)) |
112 |
| - |
113 |
| - def subscribe(self, topic_name, msgobj, cb, buffer_size=1024): |
114 |
| - assert cb is not None, "Subscribe callback is not set" |
115 |
| - |
116 |
| - #subscribing topic attributes are added |
117 |
| - self.subscribing_topics[self.id]=[msgobj,cb] |
118 |
| - |
119 |
| - #advertised if not already subscribed |
120 |
| - if topic_name not in self.advertised_topics: |
121 |
| - msg = msgobj() |
122 |
| - self._advertise_topic(topic_name, msg, 1, buffer_size) |
123 |
| - |
124 |
| - def _listen(self): |
125 |
| - while True: |
126 |
| - try: |
127 |
| - flag=self.uart.read(2) |
128 |
| - #check header |
129 |
| - if flag == b'\xff\xfe': |
130 |
| - #get bytes length |
131 |
| - lengthbyte = self.uart.read(2) |
132 |
| - length = word(list(lengthbyte)[0], list(lengthbyte)[1]) |
133 |
| - lenchk = self.uart.read(1) |
134 |
| - |
135 |
| - #validate length checksum |
136 |
| - lenchecksum = sum(list(lengthbyte)) + ord(lenchk) |
137 |
| - if lenchecksum % 256 != 255: |
138 |
| - raise ValueError('Length checksum is not right!') |
139 |
| - |
140 |
| - topic_id=list(self.uart.read(2)) |
141 |
| - inid = word(topic_id[0],topic_id[1]) |
142 |
| - if inid != 0: |
143 |
| - msgdata = self.uart.read(length) |
144 |
| - chk = self.uart.read(1) |
145 |
| - |
146 |
| - #validate topic plus msg checksum |
147 |
| - datachecksum = sum((topic_id)) + sum(list(msgdata)) + ord(chk) |
148 |
| - if datachecksum % 256 == 255: |
149 |
| - try: |
150 |
| - #incoming object msg initialized |
151 |
| - msgobj = self.subscribing_topics.get(inid)[0] |
152 |
| - except Exception : |
153 |
| - print('TX request was made or got message from not available subscribed topic.') |
154 |
| - #object sent to callback |
155 |
| - callback = self.subscribing_topics.get(inid)[1] |
156 |
| - fdata = msgobj() |
157 |
| - fdata = fdata.deserialize(msgdata) |
158 |
| - callback(fdata) |
159 |
| - else: |
160 |
| - raise ValueError('Message plus Topic ID Checksum is wrong!') |
161 |
| - |
162 |
| - except Exception as e: |
163 |
| - print('No incoming data could be read for subscribes.') |
164 |
| - |
165 |
| -#functions to be used in class |
| 61 | + register = TopicInfo() |
| 62 | + register.topic_id = self.id |
| 63 | + register.topic_name = topic_name |
| 64 | + register.message_type = msg._type |
| 65 | + register.md5sum = msg._md5sum |
| 66 | + |
| 67 | + self.advertised_topics[topic_name] = self.id |
| 68 | + |
| 69 | + # id are summed by one |
| 70 | + self.id += 1 |
| 71 | + |
| 72 | + try: |
| 73 | + register.buffer_size = buffer_size |
| 74 | + except Exception as e: |
| 75 | + print("No buffer size could be defined for topic negotiation.") |
| 76 | + |
| 77 | + # serialization |
| 78 | + packet = uio.StringIO() |
| 79 | + register.serialize(packet) |
| 80 | + |
| 81 | + # already serialized (packet) |
| 82 | + packet = list(packet.getvalue().encode("utf-8")) |
| 83 | + length = len(packet) |
| 84 | + |
| 85 | + # both checksums |
| 86 | + crclen = [checksum(le(length))] |
| 87 | + crcpack = [checksum(le(endpoint) + packet)] |
| 88 | + |
| 89 | + # final packet to be sent |
| 90 | + fpacket = header + le(length) + crclen + le(endpoint) + packet + crcpack |
| 91 | + self.uart.write(bytearray(fpacket)) |
| 92 | + |
| 93 | + def publish(self, topic_name, msg, buffer_size=1024): |
| 94 | + |
| 95 | + if topic_name not in self.advertised_topics: |
| 96 | + self._advertise_topic(topic_name, msg, 0, buffer_size) |
| 97 | + |
| 98 | + # same as advertise |
| 99 | + packet = uio.StringIO() |
| 100 | + msg.serialize(packet) |
| 101 | + |
| 102 | + packet = list(packet.getvalue().encode("utf-8")) |
| 103 | + length = len(packet) |
| 104 | + |
| 105 | + topic_id = le(self.advertised_topics.get(topic_name)) |
| 106 | + crclen = [checksum(le(length))] |
| 107 | + crcpack = [checksum(topic_id + packet)] |
| 108 | + |
| 109 | + fpacket = header + le(length) + crclen + topic_id + packet + crcpack |
| 110 | + self.uart.write(bytearray(fpacket)) |
| 111 | + |
| 112 | + def subscribe(self, topic_name, msgobj, cb, buffer_size=1024): |
| 113 | + assert cb is not None, "Subscribe callback is not set" |
| 114 | + |
| 115 | + # subscribing topic attributes are added |
| 116 | + self.subscribing_topics[self.id] = [msgobj, cb] |
| 117 | + |
| 118 | + # advertised if not already subscribed |
| 119 | + if topic_name not in self.advertised_topics: |
| 120 | + msg = msgobj() |
| 121 | + self._advertise_topic(topic_name, msg, 1, buffer_size) |
| 122 | + |
| 123 | + def _listen(self): |
| 124 | + while True: |
| 125 | + try: |
| 126 | + flag = self.uart.read(2) |
| 127 | + # check header |
| 128 | + if flag == b"\xff\xfe": |
| 129 | + # get bytes length |
| 130 | + lengthbyte = self.uart.read(2) |
| 131 | + length = word(list(lengthbyte)[0], list(lengthbyte)[1]) |
| 132 | + lenchk = self.uart.read(1) |
| 133 | + |
| 134 | + # validate length checksum |
| 135 | + lenchecksum = sum(list(lengthbyte)) + ord(lenchk) |
| 136 | + if lenchecksum % 256 != 255: |
| 137 | + raise ValueError("Length checksum is not right!") |
| 138 | + |
| 139 | + topic_id = list(self.uart.read(2)) |
| 140 | + inid = word(topic_id[0], topic_id[1]) |
| 141 | + if inid != 0: |
| 142 | + msgdata = self.uart.read(length) |
| 143 | + chk = self.uart.read(1) |
| 144 | + |
| 145 | + # validate topic plus msg checksum |
| 146 | + datachecksum = sum((topic_id)) + sum(list(msgdata)) + ord(chk) |
| 147 | + if datachecksum % 256 == 255: |
| 148 | + try: |
| 149 | + # incoming object msg initialized |
| 150 | + msgobj = self.subscribing_topics.get(inid)[0] |
| 151 | + except Exception: |
| 152 | + print( |
| 153 | + "TX request was made or got message from not available subscribed topic." |
| 154 | + ) |
| 155 | + # object sent to callback |
| 156 | + callback = self.subscribing_topics.get(inid)[1] |
| 157 | + fdata = msgobj() |
| 158 | + fdata = fdata.deserialize(msgdata) |
| 159 | + callback(fdata) |
| 160 | + else: |
| 161 | + raise ValueError("Message plus Topic ID Checksum is wrong!") |
| 162 | + |
| 163 | + except Exception as e: |
| 164 | + print("No incoming data could be read for subscribes.") |
| 165 | + |
| 166 | + |
| 167 | +# functions to be used in class |
166 | 168 | def word(l, h):
|
167 |
| - """ |
| 169 | + """ |
168 | 170 | Given a low and high bit, converts the number back into a word.
|
169 | 171 | """
|
170 |
| - return (h << 8) + l |
| 172 | + return (h << 8) + l |
| 173 | + |
171 | 174 |
|
172 |
| -#checksum method, receives array |
| 175 | +# checksum method, receives array |
173 | 176 | def checksum(arr):
|
174 |
| - return 255-((sum(arr))%256) |
175 |
| -#little-endian method |
| 177 | + return 255 - ((sum(arr)) % 256) |
| 178 | + |
| 179 | + |
| 180 | +# little-endian method |
176 | 181 | def le(h):
|
177 |
| - h &= 0xffff |
178 |
| - return [h & 0xff, h >> 8] |
| 182 | + h &= 0xFFFF |
| 183 | + return [h & 0xFF, h >> 8] |
| 184 | + |
179 | 185 |
|
180 |
| -#example code |
| 186 | +# example code |
181 | 187 | if __name__ == "__main__":
|
182 |
| - from std_msgs import String |
183 |
| - from uros import NodeHandle |
184 |
| - msg=String() |
185 |
| - msg.data= 'HiItsMeMario' |
186 |
| - node=NodeHandle(2,115200) |
187 |
| - while True: |
188 |
| - node.publish('greet',msg) |
| 188 | + from std_msgs import String |
| 189 | + from uros import NodeHandle |
| 190 | + |
| 191 | + msg = String() |
| 192 | + msg.data = "HiItsMeMario" |
| 193 | + node = NodeHandle(2, 115200) |
| 194 | + while True: |
| 195 | + node.publish("greet", msg) |
0 commit comments