rosbridge.py
Go to the documentation of this file.
1 # Copyright (c) 2018-2022, Martin Günther (DFKI GmbH) and contributors
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are met:
5 #
6 # * Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer.
8 #
9 # * Redistributions in binary form must reproduce the above copyright
10 # notice, this list of conditions and the following disclaimer in the
11 # documentation and/or other materials provided with the distribution.
12 #
13 # * Neither the name of the copyright holder nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 
29 import websocket
30 import threading
31 
32 import json
33 import traceback
34 import time
35 
36 import string
37 import random
38 
39 
41  def __init__(self, host, port):
42  self.callbacks = {}
44  self.resp = None
45  self.connection = RosbridgeWSConnection(host, port)
46  self.connection.registerCallback(self.onMessageReceived)
47 
48  def publish(self, topic, obj):
49  pub = {"op": "publish", "topic": topic, "msg": obj}
50  self.send(pub)
51 
52  def subscribe(self, topic, callback, throttle_rate=-1):
53  if self.addCallback(topic, callback):
54  sub = {"op": "subscribe", "topic": topic}
55  if throttle_rate > 0:
56  sub['throttle_rate'] = throttle_rate
57 
58  self.send(sub)
59 
60  def unhook(self, callback):
61  keys_for_deletion = []
62  for key, values in self.callbacks.items():
63  for value in values:
64  if callback == value:
65  print("Found!")
66  values.remove(value)
67  if len(values) == 0:
68  keys_for_deletion.append(key)
69 
70  for key in keys_for_deletion:
71  self.unsubscribe(key)
72  self.callbacks.pop(key)
73 
74  def unsubscribe(self, topic):
75  unsub = {"op": "unsubscribe", "topic": topic}
76  self.send(unsub)
77 
78  def callService(self, serviceName, callback=None, msg=None):
79  id = self.generate_id()
80  call = {"op": "call_service", "id": id, "service": serviceName}
81  if msg is not None:
82  call['args'] = msg
83 
84  if callback is None:
85  self.resp = None
86 
87  def internalCB(msg):
88  self.resp = msg
89  return None
90 
91  self.addServiceCallback(id, internalCB)
92  self.send(call)
93 
94  while self.resp is None:
95  time.sleep(0.01)
96 
97  return self.resp
98 
99  self.addServiceCallback(id, callback)
100  self.send(call)
101  return None
102 
103  def send(self, obj):
104  try:
105  self.connection.sendString(json.dumps(obj))
106  except Exception:
107  traceback.print_exc()
108  raise
109 
110  def generate_id(self, chars=16):
111  return ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(chars))
112 
113  def addServiceCallback(self, id, callback):
114  self.service_callbacks[id] = callback
115 
116  def addCallback(self, topic, callback):
117  if topic in self.callbacks:
118  self.callbacks[topic].append(callback)
119  return False
120 
121  self.callbacks[topic] = [callback]
122  return True
123 
124  def is_connected(self):
125  return self.connection.connected
126 
127  def is_errored(self):
128  return self.connection.errored
129 
130  def onMessageReceived(self, message):
131  try:
132  # Load the string into a JSON object
133  obj = json.loads(message)
134  # print "Received: ", obj
135 
136  if 'op' in obj:
137  option = obj['op']
138  if option == "publish": # A message from a topic we have subscribed to..
139  topic = obj["topic"]
140  msg = obj["msg"]
141  if topic in self.callbacks:
142  for callback in self.callbacks[topic]:
143  try:
144  callback(msg)
145  except Exception:
146  print("exception on callback", callback, "from", topic)
147  traceback.print_exc()
148  raise
149  elif option == "service_response":
150  if "id" in obj:
151  id = obj["id"]
152  values = obj["values"]
153  if id in self.service_callbacks:
154  try:
155  # print 'id:', id, 'func:', self.service_callbacks[id]
156  self.service_callbacks[id](values)
157  except Exception:
158  print("exception on callback ID:", id)
159  traceback.print_exc()
160  raise
161  else:
162  print("Missing ID!")
163  else:
164  print("Recieved unknown option - it was: ", option)
165  else:
166  print("No OP key!")
167  except Exception:
168  print("exception in onMessageReceived")
169  print("message", message)
170  traceback.print_exc()
171  raise
172 
173 
175  def __init__(self, host, port):
176  self.ws = websocket.WebSocketApp(
177  ("ws://%s:%d/" % (host, port)), on_message=self.on_message, on_error=self.on_error, on_close=self.on_close
178  )
179  self.ws.on_open = self.on_open
180  self.run_thread = threading.Thread(target=self.run)
181  self.run_thread.start()
182  self.connected = False
183  self.errored = False
184  self.callbacks = []
185 
186  def on_open(self):
187  print("### ROS bridge connected ###")
188  self.connected = True
189 
190  def sendString(self, message):
191  if not self.connected:
192  print("Error: not connected, could not send message")
193  # TODO: throw exception
194  else:
195  self.ws.send(message)
196 
197  def on_error(self, error):
198  self.errored = True
199  print("Error: %s" % error)
200 
201  def on_close(self):
202  self.connected = False
203  print("### ROS bridge closed ###")
204 
205  def run(self, *args):
206  self.ws.run_forever()
207 
208  def on_message(self, message):
209  # Call the handlers
210  for callback in self.callbacks:
211  callback(message)
212 
213  def registerCallback(self, callback):
214  self.callbacks.append(callback)
mir_driver.rosbridge.RosbridgeSetup.send
def send(self, obj)
Definition: rosbridge.py:103
mir_driver.rosbridge.RosbridgeWSConnection.on_message
def on_message(self, message)
Definition: rosbridge.py:208
mir_driver.rosbridge.RosbridgeSetup.connection
connection
Definition: rosbridge.py:45
mir_driver.rosbridge.RosbridgeWSConnection.run
def run(self, *args)
Definition: rosbridge.py:205
mir_driver.rosbridge.RosbridgeWSConnection.__init__
def __init__(self, host, port)
Definition: rosbridge.py:175
mir_driver.rosbridge.RosbridgeWSConnection.errored
errored
Definition: rosbridge.py:183
mir_driver.rosbridge.RosbridgeSetup.is_connected
def is_connected(self)
Definition: rosbridge.py:124
mir_driver.rosbridge.RosbridgeWSConnection.connected
connected
Definition: rosbridge.py:182
mir_driver.rosbridge.RosbridgeSetup.generate_id
def generate_id(self, chars=16)
Definition: rosbridge.py:110
mir_driver.rosbridge.RosbridgeSetup.resp
resp
Definition: rosbridge.py:44
mir_driver.rosbridge.RosbridgeSetup.unsubscribe
def unsubscribe(self, topic)
Definition: rosbridge.py:74
mir_driver.rosbridge.RosbridgeWSConnection.registerCallback
def registerCallback(self, callback)
Definition: rosbridge.py:213
mir_driver.rosbridge.RosbridgeSetup.addCallback
def addCallback(self, topic, callback)
Definition: rosbridge.py:116
mir_driver.rosbridge.RosbridgeSetup.publish
def publish(self, topic, obj)
Definition: rosbridge.py:48
mir_driver.rosbridge.RosbridgeWSConnection.on_open
def on_open(self)
Definition: rosbridge.py:186
mir_driver.rosbridge.RosbridgeWSConnection.callbacks
callbacks
Definition: rosbridge.py:184
mir_driver.rosbridge.RosbridgeSetup.unhook
def unhook(self, callback)
Definition: rosbridge.py:60
mir_driver.rosbridge.RosbridgeSetup.subscribe
def subscribe(self, topic, callback, throttle_rate=-1)
Definition: rosbridge.py:52
mir_driver.rosbridge.RosbridgeWSConnection
Definition: rosbridge.py:174
mir_driver.rosbridge.RosbridgeSetup
Definition: rosbridge.py:40
mir_driver.rosbridge.RosbridgeWSConnection.run_thread
run_thread
Definition: rosbridge.py:180
mir_driver.rosbridge.RosbridgeSetup.callbacks
callbacks
Definition: rosbridge.py:42
mir_driver.rosbridge.RosbridgeWSConnection.on_error
def on_error(self, error)
Definition: rosbridge.py:197
mir_driver.rosbridge.RosbridgeSetup.__init__
def __init__(self, host, port)
Definition: rosbridge.py:41
mir_driver.rosbridge.RosbridgeWSConnection.on_close
def on_close(self)
Definition: rosbridge.py:201
mir_driver.rosbridge.RosbridgeWSConnection.ws
ws
Definition: rosbridge.py:176
mir_driver.rosbridge.RosbridgeSetup.service_callbacks
service_callbacks
Definition: rosbridge.py:43
mir_driver.rosbridge.RosbridgeSetup.callService
def callService(self, serviceName, callback=None, msg=None)
Definition: rosbridge.py:78
mir_driver.rosbridge.RosbridgeSetup.addServiceCallback
def addServiceCallback(self, id, callback)
Definition: rosbridge.py:113
mir_driver.rosbridge.RosbridgeWSConnection.sendString
def sendString(self, message)
Definition: rosbridge.py:190
mir_driver.rosbridge.RosbridgeSetup.is_errored
def is_errored(self)
Definition: rosbridge.py:127
mir_driver.rosbridge.RosbridgeSetup.onMessageReceived
def onMessageReceived(self, message)
Definition: rosbridge.py:130


mir_driver
Author(s): Martin Günther
autogenerated on Wed Nov 13 2024 03:34:54