rosros 0.2.5
Simple unified interface to ROS1 / ROS2 Python API
Loading...
Searching...
No Matches
ros1.py
Go to the documentation of this file.
1# -*- coding: utf-8 -*-
2"""
3ROS1 core interface.
4
5------------------------------------------------------------------------------
6This file is part of rosros - simple unified interface to ROS1 / ROS2.
7Released under the BSD License.
8
9@author Erki Suurjaak
10@created 11.02.2022
11@modified 10.12.2023
12------------------------------------------------------------------------------
13"""
14## @namespace rosros.ros1
15import collections
16import datetime
17import decimal
18import functools
19import inspect
20import io
21import logging
22import os
23import re
24import shutil
25import threading
26import time
27import traceback
28
29import genpy
30import rosbag
31import roslib
32import rospy
33import rospy.core
34import rospy.names
35import rospy.rostime
36import rosservice
37
38from . import parsing
39from . import patch
40from . import rclify
41from . import util
42
43
44## `rospy.AnyMsg`
45AnyMsg = rospy.AnyMsg
46
47## ROS1 time/duration types
48ROS_TIME_TYPES = ["time", "duration"]
49
50## ROS1 time/duration types mapped to type names
51ROS_TIME_CLASSES = {rospy.Time: "time", rospy.Duration: "duration"}
52
53## Mapping between ROS type aliases and real types, like {"byte": "int8"}
54ROS_ALIAS_TYPES = {"byte": "int8", "char": "uint8"}
55
56## Separator char between ROS1 parameter namespace parts
57PARAM_SEPARATOR = "/"
58
59## Prefix for "private" names, auto-namespaced under current namespace
60PRIVATE_PREFIX = "~"
61
62## ROS Python module family, "rospy"
63FAMILY = "rospy"
64
65## Map rospy log level constants to Python logging level constants
66PY_LOG_LEVEL_TO_ROSPY_LEVEL = {
67 logging.DEBUG: 1,
68 logging.INFO: 2,
69 logging.WARN: 4,
70 logging.ERROR: 8,
71 logging.FATAL: 16,
72}
73
74
75MASTER = None
76
77
78SPINNER = None
79
80
81logger = util.ThrottledLogger(logging.getLogger(__name__))
82
83
84
85class ROSLogHandler(logging.Handler):
86 """Logging handler that forwards logging messages to ROS1 logger."""
87
88 def __init__(self):
89 super().__init__()
90 self._logger = logging.getLogger("rosout") # logging.Logger instance
91
92
93 def emit(self, record):
94 """Adds message to ROS1 logger."""
95 if not self._logger.isEnabledFor(record.levelno):
96 return
97 try:
98 text = record.msg % record.args if record.args else record.msg
99 except Exception:
100 text = record.msg
101 if record.exc_info:
102 text += "\n\n" + "".join(traceback.format_exception(*record.exc_info))
103 self._logger.log(record.levelno, text)
104
105
106
107class Bag(rosbag.Bag):
108 """
109 ROS1 bag reader and writer.
110
111 Extends `rosbag.Bag` with more conveniences, and smooths over the rosbag bug
112 of yielding messages of wrong type, if message types in different topics
113 have different packages but identical fields and hashes.
114
115 Does **not** smooth over the rosbag bug of writing different types to one topic.
116
117 rosbag does allow writing messages of different types to one topic,
118 just like live ROS topics can have multiple message types published
119 to one topic. And their serialized bytes will actually be in the bag,
120 but rosbag will only register the first type for this topic (unless it is
121 explicitly given another connection header with metadata on the other type).
122
123 All messages yielded will be deserialized by rosbag as that first type,
124 and whether reading will raise an exception or not depends on whether
125 the other type has enough bytes to be deserialized as that first type.
126 """
127
128
129 EXTENSION = ".bag"
130
131 # {(typename, typehash): message type class}
132 __TYPES = {}
133
134
135 __TYPEDEFS = {}
136
137 # {(typename, typehash): whether subtype definitions parsed}
138 __PARSEDS = {}
139
140
141 def __init__(self, f, mode="r",
142 compression=rosbag.Compression.NONE, chunk_threshold=768 * 1024,
143 allow_unindexed=False, options=None, skip_index=False):
144 """
145 Opens a ROS1 bag file.
146
147 @param f bag file path to open
148 @param mode mode to open bag in, one of "r", "w", "a"
149 @param compression bag write compression mode, one of Compression
150 @param chunk_threshold minimum number of uncompressed bytes per chunk to write
151 @param allow_unindexed allow opening unindexed bags
152 @param options optional dict with values for "compression" and "chunk_threshold"
153 @param skip_index skip reading the connection index records on open
154 """
155 super().__init__(f, mode, compression, chunk_threshold,
156 allow_unindexed, options, skip_index)
157 self.__topics = {} # {(topic, typename, typehash): message count}
158 self.__populate_meta()
159
160
161 def get_message_definition(self, msg_or_type):
162 """Returns ROS1 message type definition full text from bag, including subtype definitions."""
163 if is_ros_message(msg_or_type):
164 return self.__TYPEDEFS.get((msg_or_type._type, msg_or_type._md5sum))
165 typename = msg_or_type
166 return next((d for (n, h), d in self.__TYPEDEFS.items() if n == typename), None)
167
168
169 def get_message_class(self, typename, typehash=None):
170 """
171 Returns ROS1 message class for typename, or None if unknown type.
172
173 Generates class dynamically if not already generated.
174
175 @param typehash message type definition hash, if any
176 """
177 self.__ensure_typedef(typename, typehash)
178 typehash = typehash or next((h for n, h in self.__TYPEDEFS if n == typename), None)
179 typekey = (typename, typehash)
180 if typekey not in self.__TYPES and typekey in self.__TYPEDEFS:
181 for n, c in genpy.dynamic.generate_dynamic(typename, self.__TYPEDEFS[typekey]).items():
182 self.__TYPES[(n, c._md5sum)] = c
183 return self.__TYPES.get(typekey)
184
186 def get_message_type_hash(self, msg_or_type):
187 """Returns ROS1 message type MD5 hash."""
188 if is_ros_message(msg_or_type): return msg_or_type._md5sum
189 typename = msg_or_type
190 typehash = next((h for n, h in self.__TYPEDEFS if n == typename), None)
191 if not typehash:
192 self.__ensure_typedef(typename)
193 typehash = next((h for n, h in self.__TYPEDEFS if n == typename), None)
194 return typehash
196
197 def get_qoses(self, topic, typename):
198 """Returns None (ROS2 bag API conformity stand-in)."""
199 return None
200
201
202 def get_topic_info(self, *_, **__):
203 """Returns topic and message type metainfo as {(topic, typename, typehash): count}."""
204 return dict(self.__topics)
205
207 def read_messages(self, topics=None, start_time=None, end_time=None, connection_filter=None, raw=False):
208 """
209 Yields messages from the bag in chronological order.
210
211 @param topics list of topics or a single topic.
212 If an empty list is given, all topics will be read.
213 @param start_time earliest timestamp of messages to return,
214 as `rospy.Time` or convertible
215 (int/float/duration/datetime/decimal)
216 @param end_time latest timestamp of messages to return
217 as `rospy.Time` or convertible
218 (int/float/duration/datetime/decimal)
219 @param connection_filter function to filter connections to include
220 @param raw if True, then returned messages are tuples of
221 (typename, bytes, md5sum, typeclass)
222 or (typename, bytes, md5sum, position, typeclass),
223 depending on file format version
224 @return generator of BagMessage(topic, message, timestamp) namedtuples
225 """
226 hashtypes = {}
227 for n, h in self.__TYPEDEFS: hashtypes.setdefault(h, []).append(n)
228 read_topics = topics if isinstance(topics, list) else [topics] if topics else None
229 dupes = {t: (n, h) for t, n, h in self.__topics
230 if (read_topics is None or t in read_topics) and len(hashtypes.get(h, [])) > 1}
231
232 kwargs = dict(topics=topics, start_time=to_time(start_time), end_time=to_time(end_time),
233 connection_filter=connection_filter, raw=raw)
234 if not dupes:
235 for topic, msg, stamp in super().read_messages(**kwargs):
236 yield rosbag.bag.BagMessage(topic, msg, stamp)
237 return
238
239 for topic, msg, stamp in super().read_messages(**kwargs):
240 # Workaround for rosbag bug of using wrong type for identical type hashes
241 if topic in dupes:
242 typename, typehash = (msg[0], msg[2]) if raw else (msg._type, msg._md5sum)
243 if dupes[topic] != (typename, typehash):
244 if raw:
245 msg = msg[:-1] + (self.get_message_class(typename, typehash), )
246 else:
247 msg = self.__convert_message(msg, *dupes[topic])
248 yield rosbag.bag.BagMessage(topic, msg, stamp)
249
250
251 def write(self, topic, msg, t=None, raw=False, connection_header=None, **__):
252 """
253 Writes a message to the bag.
254
255 @param topic name of topic
256 @param msg ROS message to write, or tuple if raw
257 @param t message timestamp if not using current wall time,
258 as `rospy.Time` or convertible
259 (int/float/duration/datetime/decimal)
260 @param raw if true, msg is expected
261 as (typename, bytes, typehash, typeclass)
262 or (typename, bytes, typehash, position, typeclass)
263 @param connection_header custom connection header dict if any,
264 as {"topic", "type", "md5sum", "message_definition"}
265 """
266 return super().write(topic, msg, to_time(t), raw, connection_header)
267
268
269 def __convert_message(self, msg, typename2, typehash2=None):
270 """Returns message converted to given type; fields must match."""
271 msg2 = self.get_message_class(typename2, typehash2)()
272 fields2 = get_message_fields(msg2)
273 for fname, ftypename in get_message_fields(msg).items():
274 v1 = v2 = getattr(msg, fname)
275 if ftypename != fields2.get(fname, ftypename):
276 v2 = self.__convert_message(v1, fields2[fname])
277 setattr(msg2, fname, v2)
278 return msg2
279
281 def __populate_meta(self):
282 """Populates topics and message type definitions and hashes."""
283 result = collections.Counter() # {(topic, typename, typehash): count}
284 counts = collections.Counter() # {connection ID: count}
285 for c in self._chunks:
286 for c_id, count in c.connection_counts.items():
287 counts[c_id] += count
288 for c in self._connections.values():
289 result[(c.topic, c.datatype, c.md5sum)] += counts[c.id]
290 self.__TYPEDEFS[(c.datatype, c.md5sum)] = c.msg_def
291 self.__topics = dict(result)
292
293
294 def __ensure_typedef(self, typename, typehash=None):
295 """Parses subtype definition from any full definition where available, if not loaded."""
296 typehash = typehash or next((h for n, h in self.__TYPEDEFS if n == typename), None)
297 typekey = (typename, typehash)
298 if typekey not in self.__TYPEDEFS:
299 for (roottype, roothash), rootdef in list(self.__TYPEDEFS.items()):
300 rootkey = (roottype, roothash)
301 if self.__PARSEDS.get(rootkey): continue # for (roottype, roothash)
302
303 subdefs = tuple(parsing.parse_definition_subtypes(rootdef).items())
304 subhashes = {n: parsing.calculate_definition_hash(n, d, subdefs)
305 for n, d in subdefs}
306 self.__TYPEDEFS.update(((n, subhashes[n]), d) for n, d in subdefs)
307 self.__PARSEDS.update(((n, h), True) for n, h in subhashes.items())
308 self.__PARSEDS[rootkey] = True
309 if typekey in self.__TYPEDEFS:
310 break # for (roottype, roothash)
311 self.__TYPEDEFS.setdefault(typekey, "")
312
313
314 @staticmethod
315 def reindex_file(f):
316 """
317 Reindexes bag file on disk.
318
319 Makes a temporary copy in file directory.
320 """
321 with rosbag.Bag(f, allow_unindexed=True, skip_index=True) as inbag:
322 inplace = (inbag.version > 102)
323
324 f2 = util.unique_path("%s.orig%s") % os.path.splitext(f)
325 shutil.copy(f, f2)
326 inbag, outbag = None, None
327 try:
328 inbag = rosbag.Bag(f2, allow_unindexed=True) if not inplace else None
329 outbag = rosbag.Bag(f, mode="a" if inplace else "w", allow_unindexed=True)
330 # v102: build index from inbag, write all messages to outbag.
331 # Later versions: re-build index in outbag file in-place.
332 for _ in (outbag if inplace else inbag).reindex(): pass
333 if not inplace:
334 for (topic, msg, t, header) in inbag.read_messages(return_connection_header=True):
335 outbag.write(topic, msg, t, connection_header=header)
336 except BaseException: # Ensure steady state even on KeyboardInterrupt/SystemExit
337 inbag and inbag.close()
338 outbag and outbag.close()
339 shutil.move(f2, f) # Restore original from temporary copy
340 raise
341 inbag and inbag.close()
342 outbag and outbag.close()
343 os.remove(f2) # Drop temporary copy
344
345
346
347class Mutex:
348 """Container for local mutexes."""
349
350
351 SPIN = threading.RLock()
352
353
354 SPIN_START = threading.RLock()
355
356
357
358def init_node(name, args=None, namespace=None, anonymous=False, log_level=None, enable_rosout=True):
359 """
360 Initializes rospy and creates ROS1 node.
361
362 @param name node name, without namespace
363 @param args list of command-line arguments for the node
364 @param namespace node namespace override
365 @param anonymous whether to auto-generate a unique name for the node,
366 using the given name as base
367 @param log_level level to set for ROS logging
368 (name like "DEBUG" or one of `logging` constants like `logging.DEBUG`)
369 @param enable_rosout `False` to suppress auto-publication of rosout
370 """
371 global MASTER, logger
372 if MASTER: return
373
374 if namespace and namespace != "/":
375 os.environ["ROS_NAMESPACE"] = namespace
376 rospy.names._set_caller_id(util.namejoin(namespace, name))
377
378 ros_level = None
379 if log_level is not None:
380 if isinstance(log_level, str): log_level = logging.getLevelName(log_level)
381 ros_level = PY_LOG_LEVEL_TO_ROSPY_LEVEL.get(log_level)
382
383 patch.patch_ros1()
384 logger.debug("Initializing ROS node %r.", name)
385 rospy.init_node(name, args, anonymous=anonymous, log_level=ros_level,
386 disable_rosout=not enable_rosout, disable_signals=True)
387 MASTER = rospy.client.get_master()
388
389 if not any(isinstance(x, ROSLogHandler) for x in logger.handlers):
390 logger.addHandler(ROSLogHandler())
391 if log_level is not None:
392 logger.setLevel(log_level)
393
394
395def register_init():
396 """Informs `rosros` of ROS1 having been initialized outside of `init_node()`."""
397 global MASTER
398 if MASTER: return
399
400 patch.patch_ros1()
401 MASTER = rospy.client.get_master()
402 if not any(isinstance(x, ROSLogHandler) for x in logger.handlers):
403 logger.addHandler(ROSLogHandler())
404
405
406def init_params(defaults=None, **defaultkws):
407 """
408 Declares all parameters on node from defaults dictionary.
409
410 @param defaults nested dictionary with suitable keys and values for ROS
411 (keys must be valid namespace names,
412 list values must not contain nested values)
413 @param defaultkws parameters as key=value
414 @return full nested parameters dictionary, combined from given defaults
415 and externally set parameters on the node
416 """
417 result = {}
418 stack = list(util.merge_dicts(defaults or {}, defaultkws).items())
419 while stack:
420 k, v = stack.pop()
421 if isinstance(v, dict):
422 if not v:
423 util.set_value(result, k.split(PARAM_SEPARATOR), v)
424 stack.extend(("%s%s%s" % (k, PARAM_SEPARATOR, k2), v2) for k2, v2 in v.items())
425 else:
426 k = "~" + format_param_name(k)
427 if not rospy.has_param(k):
428 rospy.set_param(k, v)
429
430 prefix = get_node_name().rstrip(PARAM_SEPARATOR) + PARAM_SEPARATOR
431 for path in rospy.get_param_names():
432 if path.startswith(prefix):
433 path = path[len(prefix):]
434 util.set_value(result, [x for x in path.split(PARAM_SEPARATOR) if x],
435 rospy.get_param("~" + path))
436 logger.debug("Initialized node parameters %r.", result)
437 return result
438
439
440def has_param(name):
441 """
442 Returns whether the parameter exists.
443
444 @param name full name of the parameter in node namespace
445 """
446 return rospy.has_param("~" + format_param_name(name))
447
448
449def get_param(name, default=None, autoset=True):
450 """
451 Returns parameter value from current ROS1 node.
452
453 @param name full name of the parameter in node namespace
454 @param default optional default to return if parameter unknown
455 @param autoset set default value to node parameter if unknown
456 @return parameter value, or default if parameter was unknown
457
458 @throws KeyError if parameter not set and default not given
459 """
460 name = "~" + format_param_name(name)
461 if autoset and default is not None and not rospy.has_param(name):
462 rospy.set_param(name, default)
463 return rospy.get_param(name, default)
464
466def get_param_names():
467 """Returns the names of all current ROS1 node parameters."""
468 result = []
469 prefix = get_node_name().rstrip(PARAM_SEPARATOR) + PARAM_SEPARATOR
470 for path in rospy.get_param_names():
471 if path.startswith(prefix): result.append(path[len(prefix):])
472 return result
473
474
475def get_params(nested=True):
476 """
477 Returns the current ROS1 node parameters, by default as nested dictionary.
479 @param nested return a nested dictionary,
480 like `{"my": {"name": 1}}` vs {"my.name": 1}
481 """
482 result = {}
483 prefix = get_node_name().rstrip(PARAM_SEPARATOR) + PARAM_SEPARATOR
484 for path in rospy.get_param_names():
485 if path.startswith(prefix):
486 path = path[len(prefix):]
487 key = [x for x in path.split(PARAM_SEPARATOR) if x] if nested else path
488 util.set_value(result, key, rospy.get_param("~" + path))
489 return result
490
491
492def set_param(name, value):
493 """
494 Sets a parameter on the node.
495
496 @param name full name of the parameter in node namespace
497 @param value parameter value to set
498 @return the set value
499 """
500 rospy.set_param("~" + format_param_name(name), value)
501 return value
502
503
504def delete_param(name):
505 """
506 Deletes parameter from the node.
507
508 @param name full name of the parameter in node namespace
509 @throws KeyError if parameter not set
510 """
511 rospy.delete_param("~" + format_param_name(name))
512
513
514def ok():
515 """Returns whether ROS1 has been initialized and is not shut down."""
516 return rospy.core.is_initialized() and \
517 not (rospy.is_shutdown() or rospy.core.is_shutdown_requested())
518
519
520def on_shutdown(callback, *args, **kwargs):
521 """
522 Registers function to be called on shutdown, after node has been torn down.
523
524 Function is called with given arguments.
525 """
526 if args or kwargs: callback = functools.partial(callback, *args, **kwargs)
527 rospy.on_shutdown(callback)
528
529
531 """
532 Sets ROS1 spinning forever in a background thread.
533
534 Does nothing if spinning has already been started.
535 """
536 def do_spin():
537 global SPINNER
538 try:
539 while SPINNER and ok(): spin_once(0.5)
540 finally: SPINNER = None
542 global SPINNER
543 with Mutex.SPIN_START:
544 if SPINNER or Mutex.SPIN._is_owned(): return
545 SPINNER = threading.Thread(target=do_spin)
546 SPINNER.start()
547
548
549def spin():
550 """Spins ROS1 forever."""
551 global SPINNER
552
553 if SPINNER:
554 try: SPINNER.join() # Wait on background thread instead
555 except KeyboardInterrupt: SPINNER = None # Signal thread to end
556 return
557
558 if Mutex.SPIN._is_owned(): # spin() started from another thread
559 with Mutex.SPIN: return # Wait on mutex instead
560 with Mutex.SPIN:
561 rospy.spin()
562
563
564def spin_once(timeout=None):
565 """
566 Waits until timeout, or forever if timeout None or negative.
567
568 @param timeout time to sleep, as seconds or ROS duration;
569 None or <0 waits forever
570 """
571 rospy.rostime.wallsleep(2**31 - 1 if timeout is None or timeout < 0 else to_sec(timeout))
572
573
574def spin_until_future_complete(future, timeout=None):
575 """
576 Spins ROS1 until future complete or timeout reached or ROS shut down.
577
578 @param future object with `concurrent.futures.Future`-conforming interface to complete
579 @param timeout time to wait, as seconds or ROS1 duration
580 """
581 if future.done():
582 return
583 timeout = to_sec(timeout)
584 if timeout == 0.0:
585 if not future.done():
586 spin_once(1E-9)
587 return
588
589 now = time.monotonic()
590 deadline = (now + timeout) if timeout is not None and timeout >= 0 else -1
591 while ok() and (deadline < 0 or now < deadline) and not future.done():
592 spin_once(0.1)
593 now = time.monotonic()
594 if not future.done() and not ok():
595 future.cancel("ROS shut down")
596
597
598def shutdown(reason=None):
599 """
600 Sends the shutdown signal to rospy.
601
602 @param reason shutdown reason to log, if any
603 """
604 rospy.signal_shutdown(reason or "")
605
606
607def create_client(service, cls_or_typename):
608 """
609 Returns a ROS1 service client instance, for invoking a service.
610
611 @param service name of service to invoke
612 @param cls_or_typename ROS1 service class object like `std_srvs.srv.SetBool`
613 or service type name like `"std_srvs/SetBool"
614 @return `rospy.ServiceProxy`,
615 will have `call_async()` returning a future
616 """
617 return rospy.ServiceProxy(service, get_message_class(cls_or_typename) )
619
620def create_service(service, cls_or_typename, callback):
621 """
622 Returns a ROS1 service server instance, for providing a service.
623
624 @param service name of service to provide
625 @param cls_or_typename ROS1 service class object like `std_srvs.srv.SetBool`
626 or service type name like `"std_srvs/SetBool"
627 @param callback callback function, invoked with (req, resp) or (req)
628 @return `rospy.Service`
629 """
630 cls = get_message_class(cls_or_typename)
631 arity = util.get_arity(callback)
632 if arity in (-1, 2):
633 mycallback = functools.partial((lambda f, c, r: f(r, c._response_class())), callback, cls)
634 else:
635 mycallback = util.wrap_arity(callback) if arity != 1 else callback
636 return rospy.Service(service, cls, mycallback)
637
638
639def create_publisher(topic, cls_or_typename, latch=False, queue_size=0):
640 """
641 Returns a ROS1 publisher instance.
643 @param topic name of topic to open
644 @param cls_or_typename ROS1 message class object like `std_msgs.msg.Bool`
645 or message type name like "std_msgs/Bool"
646 @param latch provide last published message to new subscribers
647 @param queue_size queue size of outgoing messages (0 or None: infinite)
648 @return `rospy.Publisher`
649 """
650 cls = get_message_class(cls_or_typename)
651 return rospy.Publisher(topic, cls, latch=latch, queue_size=queue_size or 0)
652
653
654def create_subscriber(topic, cls_or_typename, callback, callback_args=None,
655 queue_size=0, raw=False):
656 """
657 Returns a ROS1 subscriber instance.
658
659 @param topic name of topic to listen to
660 @param cls_or_typename ROS1 message class object like `std_msgs.msg.Bool`
661 or message type name like "std_msgs/Bool"
662 @param callback callback function, invoked with received message,
663 and with additional arguments if given
664 @param callback_args additional arguments to pass to the callback, if any,
665 invoked as `callback(msg, callback_args)´
666 @param raw make subscription with AnyMsg,
667 invoke callback with serialized message bytes
668 @param queue_size queue size of incoming messages (0 or None: infinite)
669 @return `rospy.Subscriber`
670 """
671 cls = get_message_class(cls_or_typename)
672 anymsg = issubclass(cls, rospy.AnyMsg) and not raw # Return AnyMsg if not explicitly raw
673 if raw and not issubclass(cls, rospy.AnyMsg): cls = rospy.AnyMsg
674 sub = rospy.Subscriber(topic, cls, callback, callback_args, queue_size=queue_size or None)
675 sub._anymsg = anymsg
676 return sub
677
678
679def create_timer(period, callback, oneshot=False, immediate=False):
680 """
681 Returns a ROS1 timer instance.
682
683 @param period desired period between callbacks, as seconds or ROS1 duration
684 @param callback callback function invoked on timer, with no arguments
685 @param oneshot whether to fire only once
686 @param immediate whether to fire once immediately instead of waiting one period
687 @return `rospy.Timer`
688 """
689 period, mycallback = make_duration(period), lambda *_, **__: callback()
690 if immediate:
691 timer = rospy.Timer(rospy.Duration(nsecs=1), mycallback, oneshot=True)
692 if not immediate or not oneshot:
693 timer = rospy.Timer(period, mycallback, oneshot)
694 return timer
695
696
697def create_rate(frequency):
698 """
699 Returns a ROS1 rate instance, for sleeping at a fixed rate.
700
701 @param frequency rate to sleep at, in Hz
702 @return `rospy.Rate`
703 """
704 return rospy.Rate(frequency)
705
706
707def destroy_entity(item):
708 """Closes the given publisher, subscriber, service client, or service server instance."""
709 if isinstance(item, rospy.topics.Topic):
710 item.unregister()
711 elif isinstance(item, rospy.client.ServiceProxy):
712 item.close()
713 elif callable(getattr(item, "shutdown", None)):
714 item.shutdown() # Service or timer
715
717def get_namespace():
718 """Returns ROS1 node namespace."""
719 return rospy.get_namespace()
720
721
722def get_node_name():
723 """Returns ROS1 node full name with namespace."""
724 return rospy.get_name()
725
727def get_nodes():
728 """Returns all ROS1 nodes, as `[node full name, ]`."""
729 # getSystemState() returns [[topic, [publishing node, ]], [..subs], [..services]]
730 return sorted(set(l for s in MASTER.getSystemState()[-1] for _, ll in s for l in ll))
732
733def get_topics():
734 """Returns all available ROS1 topics, as `[(topic name, [type name, ]), ]`."""
735 result = {}
736 for n, t in MASTER.getTopicTypes()[-1]:
737 result.setdefault(n, []).append(t)
738 return sorted([n, sorted(map(canonical, tt))] for n, tt in result.items())
739
740
741def get_services(node=None, namespace=None, include_types=True):
742 """
743 Returns all available ROS1 services, as `[(service name, [type name, ]), ]`.
744
745 @param node full name of the node to return services for, if any
746 @param namespace full or partial namespace to scope services from
747 @param include_types if false, type names will be returned as an empty list
748 """
749 result = []
750 for name in rosservice.get_service_list(node, namespace):
751 if not include_types:
752 result.append([name, []])
753 continue # for name
754 try:
755 typename = rosservice.get_service_type(name)
756 if typename: result.append([name, [typename]])
757 except Exception:
758 logger.warning("Error retrieving service type for %s.", name, exc_info=True)
759 return sorted(result)
760
761
762def get_logger():
763 """
764 Returns `logging.Logger` for logging to ROS1 log handler.
765
766 Logging methods on the logger (`debug()`, `info()`, etc) accept additional keyword arguments:
767 - `__once__`: whether to log only once from call site
768 - `__throttle__`: seconds to skip logging from call site for
769 - `__throttle_identical__`: whether to skip logging identical consecutive texts from call site
770 (given log message excluding formatting arguments).
771 Combines with `__throttle__` to skip duplicates for a period.
772 """
773 return logger
774
775
776def get_rostime():
777 """Returns current ROS1 time, as `rospy.Time`."""
778 result = rospy.get_rostime()
779 if patch.PATCHED_FULL and not rospy.rostime.is_wallclock() \
780 and rclify.clock.ClockType.ROS_TIME != result.clock_type:
781 patch.set_extra_attribute(result, "_clock_type", rclify.clock.ClockType.ROS_TIME)
782 return result
783
784
785def remap_name(name, namespace=None):
786 """
787 Returns the absolute remapped topic/service name if mapping exists.
788
789 @param name name to seek exact remapping for
790 @param namespace namespace to resolve relative and private names to,
791 by default current node namespace
792 @return remapped resolved name, or original if not mapped
793 """
794 return rospy.remap_name(name, namespace)
795
796
797def resolve_name(name, namespace=None):
798 """
799 Returns absolute remapped name, namespaced under current node if relative or private.
800
801 @param namespace namespace to use if not current node full name
802 """
803 namespace = namespace or get_node_name()
804 if not name: # empty string resolves to node namespace
805 return util.namesplit(namespace)[0] + "/"
806
807 # Discard multiple slashes
808 name2 = ("/" if name.startswith("/") else "") + "/".join(filter(bool, name.split("/")))
809 if name2.startswith("~"): # private name
810 name2 = util.namejoin(namespace, name2.lstrip("~"))
811 elif not name2.startswith("/"): # relative name
812 name2 = util.namejoin(util.namesplit(namespace)[0], name2)
813
814 return rospy.names.get_resolved_mappings().get(name2, name2)
815
816
817def sleep(duration):
818 """
819 Sleeps for the specified duration in ROS time.
820
821 Raises error on ROS shutdown or ROS time jumping backwards
822
823 @param duration time to sleep, as seconds or ROS duration, <=0 returns immediately
824 """
825 rospy.sleep(to_duration(duration))
826
827
828def wait_for_publisher(topic, timeout=None, cls_or_typename=None):
829 """
830 Blocks until topic has at least one publisher.
831
832 @param topic name of topic to open
833 @param timeout time to wait at most, as seconds or ROS duration;
834 None or <0 waits forever
835 @param cls_or_typename message type to expect if any,
836 as ROS message class object like `std_msgs.msg.Bool`
837 or message type name like "std_msgs/Bool"
838 @return whether a publisher is available
839 """
840 result = False
841 timeout = to_sec(timeout)
842 deadline = None if timeout is None or timeout < 0 else time.monotonic() + timeout
843 typename = get_message_type(cls_or_typename) or cls_or_typename
844 if "*" == typename: typename = None # AnyMsg
845 fullname, first = resolve_name(topic), True
846 while not result and (first or deadline is None or time.monotonic() < deadline):
847 exists, first = any(fullname == t for t, nn in MASTER.getSystemState()[-1][0]), False
848 result = exists and (not typename or typename in dict(get_topics()).get(fullname, []))
849 rospy.rostime.wallsleep(0.1) if not result else None
850 return result
851
852
853def wait_for_subscriber(topic, timeout=None, cls_or_typename=None):
854 """
855 Blocks until topic has at least one subscriber.
856
857 @param topic name of topic to open
858 @param timeout time to wait at most, as seconds or ROS duration;
859 None or <0 waits forever
860 @param cls_or_typename message type to expect if any,
861 as ROS message class object like `std_msgs.msg.Bool`
862 or message type name like "std_msgs/Bool"
863 @return whether a subscriber is available
864 """
865 result = False
866 timeout = to_sec(timeout)
867 deadline = None if timeout is None or timeout < 0 else time.monotonic() + timeout
868 typename = get_message_type(cls_or_typename) or cls_or_typename
869 if "*" == typename: typename = None # AnyMsg
870 fullname, first = resolve_name(topic), True
871 while not result and (first or deadline is None or time.monotonic() < deadline):
872 exists, first = any(fullname == t for t, nn in MASTER.getSystemState()[-1][1]), False
873 result = exists and (not typename or typename in dict(get_topics()).get(fullname, []))
874 rospy.rostime.wallsleep(0.1) if not result else None
875 return result
876
877
878def wait_for_service(service, timeout=None, cls_or_typename=None):
879 """
880 Blocks until service is available.
881
882 @param service name of service
883 @param timeout time to wait at most, as seconds or ROS duration;
884 None or <0 waits forever
885 @param cls_or_typename service type to expect if any,
886 as ROS service class object like `std_msgs.msg.Bool`
887 or service type name like "std_srvs/SetBool"
888 @return whether the service is available
889 """
890 result = False
891 timeout = None if timeout is None or to_sec(timeout) <= 0 else to_sec(timeout)
892 deadline = None if timeout is None else time.monotonic() + timeout
893 typename = get_message_type(cls_or_typename) or cls_or_typename
894 fullname, first = resolve_name(service), True
895 while not result and (first or deadline is None or time.monotonic() < deadline):
896 first = False
897 try: rospy.wait_for_service(service, timeout)
898 except Exception: continue # while
899 result = not typename
900 if typename:
901 srvs = dict(get_services())
902 result = typename in srvs.get(fullname, [])
903 if not result and fullname in srvs: rospy.rostime.wallsleep(0.1) # Type mismatch
904 return result
905
906
907# -------------------------------- GENERAL API --------------------------------
908
909
910@util.memoize
911def canonical(typename):
912 """Returns "pkg/Type" for "pkg/subdir/Type"."""
913 if typename.count("/") > 1:
914 typename = "%s/%s" % tuple((x[0], x[-1]) for x in [typename.split("/")])[0]
915 return typename
917
918@util.memoize
919def format_param_name(name):
920 """Returns parameter name using "/" separator, and leading root or private sigils stripped."""
921 return name.replace(".", PARAM_SEPARATOR).lstrip("~" + PARAM_SEPARATOR)
922
923
924def get_message_class(msg_or_type):
925 """
926 Returns ROS1 message / service class object.
927
928 @param msg_or_type full or canonical class name
929 like "std_msgs/Bool" or "std_srvs/SetBool" or "std_srvs/SetBoolRequest",
930 or class instance like `std_msgs.msg.Bool()`,
931 or class object like `std_msgs.msg.Bool`
932 @return ROS1 message / service class object, like `std_msgs.msg.Bool`
933 or `std_srvs.srv.SetBool` or `std_srvs.srv.SetBoolRequest`,
934 or None if not found
935 """
936 if is_ros_message(msg_or_type) or is_ros_service(msg_or_type):
937 return msg_or_type if inspect.isclass(msg_or_type) else type(msg_or_type)
938
939 typename, cls = canonical(msg_or_type), None
940 if typename.startswith("%s/" % FAMILY):
941 cls = next((c for c in ROS_TIME_CLASSES if get_message_type(c) == typename), None)
942 try: cls = cls or roslib.message.get_message_class(typename)
943 except Exception: cls = None
944 if not cls:
945 try: cls = roslib.message.get_service_class(typename)
946 except Exception: cls = None
947 if not cls and re.match(r".+_(Request|Response)$", typename):
948 # Smooth over ROS1/ROS2 difference: ServiceRequest vs Service_Request
949 typename = re.sub(r"(.+)_(Request|Response)$", r"\1\2", typename)
950 try: cls = roslib.message.get_service_class(typename)
951 except Exception: cls = None
952 return cls
953
954
955def get_message_definition(msg_or_type, full=True):
956 """
957 Returns ROS1 message or service request/response type definition text.
958
959 Text will include subtype definitions by default.
960
961 @param msg_or_type canonical or full class name like "std_msgs/Bool" or "std_msgs/msg/Bool",
962 or class instance like `std_msgs.msg.Bool()`,
963 or class object like `std_msgs.msg.Bool`
964 @param full include definitions of nested types, separated by "\n---\nMSG: pkg/Type\n"
965 (ignored for service request/response types)
966 @return message type definition text
967 """
968 text = get_message_class(msg_or_type)._full_text
969 if not full: # Discard everything after a full line of "="
970 text = re.sub(r"\n=+\n.+", "", text, flags=re.DOTALL)
971 return text
972
973
974def get_message_fields(val):
975 """
976 Returns {field name: field type name} if ROS1 message or service request/response, else {}.
977
978 @param val ROS1 message or service request/response instance, or class object
979 """
980 names = getattr(val, "__slots__", [])
981 if isinstance(val, tuple(ROS_TIME_CLASSES)): # Empty __slots__
982 names = genpy.TVal.__slots__
983 return dict(zip(names, getattr(val, "_slot_types", [])))
984
985
986def get_message_header(val):
987 """
988 Returns message `std_msgs/Header`-attribute if any, else `None`.
989
990 @param val ROS message or service request/response instance
991 """
992 if is_ros_message(val) and val._has_header:
993 for name, typename in get_message_fields(val):
994 if "std_msgs/Header" == typename:
995 return get_message_value(val, name)
996 return None
997
998
999def get_message_type(msg_or_cls):
1000 """
1001 Returns ROS1 message / service canonical type name, like "std_msgs/Header".
1002
1003 Returns "*" for `AnyMsg`.
1004
1005 @param msg_or_cls class instance like `std_msgs.msg.Bool()`,
1006 or class object like `std_msgs.msg.Bool`
1007 @return canonical name, or `None` if not ROS message / service
1008 """
1009 if is_ros_time(msg_or_cls):
1010 cls = msg_or_cls if inspect.isclass(msg_or_cls) else type(msg_or_cls)
1011 return "%s/%s" % (FAMILY, cls.__name__)
1012 return msg_or_cls._type if is_ros_message(msg_or_cls) or is_ros_service(msg_or_cls) else None
1013
1014
1015def get_message_type_hash(msg_or_type):
1016 """
1017 Returns ROS1 message / service type MD5 hash.
1018
1019 @param msg_or_type full or canonical class name
1020 like "std_msgs/Bool" or "std_srvs/SetBool" or "std_srvs/SetBoolRequest",
1021 or class instance like `std_msgs.msg.Bool()`,
1022 or class object like `std_msgs.msg.Bool`
1023 """
1024 return get_message_class(msg_or_type)._md5sum
1025
1026
1027def get_message_value(msg, name, default=...):
1028 """
1029 Returns object attribute value, with numeric arrays converted to lists.
1030
1031 @param name message attribute name; may also be (nested, path) or "nested.path"
1032 @param default value to return if attribute does not exist; raises exception otherwise
1033 """
1034 try: v, parent, k = util.get_nested(msg, name)
1035 except Exception:
1036 if default is not Ellipsis: return default
1037 raise
1038 if isinstance(v, bytes) and is_ros_message(parent) \
1039 and get_message_fields(parent)[k].startswith("uint8["):
1040 v = list(v)
1041 return v
1042
1043
1044def get_service_definition(srv_or_type):
1045 """
1046 Returns ROS1 service type definition text.
1047
1048 @param srv_or_type canonical or full class name
1049 like "std_srvs/SetBool" or "std_srvs/srv/SetBool",
1050 or class instance like `std_srvs.srv.SetBool()`,
1051 or class object like `std_srvs.srv.SetBool`
1052 @return ROS service type definition text
1053 """
1054 cls = get_message_class(srv_or_type)
1055 reqcls, respcls = cls._request_class, cls._response_class
1056 reqtxt, resptxt = (get_message_definition(x, full=False) for x in (reqcls, respcls))
1057 return "%s\n---\n%s" % (reqtxt, resptxt)
1058
1059
1060def get_service_request_class(srv_or_type):
1061 """
1062 Returns ROS1 service request class object.
1063
1064 @param srv_or_type canonical or full class name
1065 like "std_srvs/SetBool" or "std_srvs/srv/SetBool",
1066 or class instance like `std_srvs.srv.SetBool()`,
1067 or class object like `std_srvs.srv.SetBool`
1068 @return ROS1 service request class object, like `std_srvs.srv.SetBoolRequest`
1069 """
1070 return get_message_class(srv_or_type)._request_class
1071
1072
1073def get_service_response_class(srv_or_type):
1074 """
1075 Returns ROS1 service response class object.
1076
1077 @param srv_or_type canonical or full class name
1078 like "std_srvs/SetBool" or "std_srvs/srv/SetBool",
1079 or class instance like `std_srvs.srv.SetBool()`,
1080 or class object like `std_srvs.srv.SetBool`
1081 @return ROS1 service response class object, like `std_srvs.srv.SetBoolResponse`
1082 """
1083 return get_message_class(srv_or_type)._response_class
1084
1085
1086def is_ros_message(val):
1087 """
1088 Returns whether value is a ROS1 message or service request/response class or instance.
1089
1090 @param val like `std_msgs.msg.Bool()` or `std_srvs.srv.SetBoolRequest`
1091 @return True if value is a ROS1 message or service request/response class or instance,
1092 False otherwise
1093 """
1094 return (issubclass if inspect.isclass(val) else isinstance)(val, (genpy.Message, genpy.TVal))
1095
1096
1097def is_ros_service(val):
1098 """Returns whether value is a ROS1 service class object or instance."""
1099 STRFIELDS, MSGFIELDS = ('_md5sum', '_type'), ('_request_class', '_response_class')
1100 return all(isinstance(getattr(val, x, None), str) for x in STRFIELDS) and \
1101 all(is_ros_message(getattr(val, x, None)) for x in MSGFIELDS)
1102
1103
1104def is_ros_time(val):
1105 """Returns whether value is a ROS1 time/duration class or instance."""
1106 return issubclass(val, genpy.TVal) if inspect.isclass(val) else isinstance(val, genpy.TVal)
1107
1108
1109def make_duration(secs=0, nsecs=0):
1110 """Returns a ROS1 duration, as rospy.Duration."""
1111 return rospy.Duration(secs=secs, nsecs=nsecs)
1112
1113
1114def make_time(secs=0, nsecs=0):
1115 """Returns a ROS1 time, as rospy.Time."""
1116 return rospy.Time(secs=secs, nsecs=nsecs)
1117
1118
1119def serialize_message(msg):
1120 """Returns ROS1 message or service request/response as a serialized binary of `bytes()`."""
1121 buf = io.BytesIO()
1122 msg.serialize(buf)
1123 return buf.getvalue()
1124
1125
1126def deserialize_message(raw, cls_or_typename):
1127 """Returns ROS1 message or service request/response instantiated from serialized binary."""
1128 return get_message_class(cls_or_typename)().deserialize(raw)
1129
1130
1131@util.memoize
1132def scalar(typename):
1133 """
1134 Returns scalar type from ROS message data type, like "uint8" from "uint8[100]".
1135
1136 Returns type unchanged if already a scalar.
1137 """
1138 return typename[:typename.index("[")] if "[" in typename else typename
1139
1140
1141def to_duration(val):
1142 """Returns value as ROS1 duration if convertible (int/float/time/datetime/decimal), else value."""
1143 result = val
1144 if isinstance(val, decimal.Decimal):
1145 result = rospy.Duration(int(val), float(val % 1) * 10**9)
1146 elif isinstance(val, datetime.datetime):
1147 result = rospy.Duration(int(val.timestamp()), 1000 * val.microsecond)
1148 elif isinstance(val, (float, int)):
1149 result = rospy.Duration(val)
1150 elif isinstance(val, rospy.Time):
1151 result = rospy.Duration(val.secs, val.nsecs)
1152 return result
1153
1154
1155def to_nsec(val):
1156 """Returns value in nanoseconds if value is ROS1 time/duration, else value."""
1157 return val.to_nsec() if isinstance(val, genpy.TVal) else val
1158
1159
1160def to_sec(val):
1161 """Returns value in seconds if value is ROS1 time/duration, else value."""
1162 return val.to_sec() if isinstance(val, genpy.TVal) else val
1163
1164
1165def to_sec_nsec(val):
1166 """Returns value as (seconds, nanoseconds) if value is ROS1 time/duration, else value."""
1167 return (val.secs, val.nsecs) if isinstance(val, genpy.TVal) else val
1168
1169
1170def to_time(val):
1171 """Returns value as ROS1 time if convertible (int/float/duration/datetime/decimal), else value."""
1172 result = val
1173 if isinstance(val, decimal.Decimal):
1174 result = rospy.Time(int(val), float(val % 1) * 10**9)
1175 elif isinstance(val, datetime.datetime):
1176 result = rospy.Time(int(val.timestamp()), 1000 * val.microsecond)
1177 elif isinstance(val, (float, int)):
1178 result = rospy.Time(val)
1179 elif isinstance(val, rospy.Duration):
1180 result = rospy.Time(val.secs, val.nsecs)
1181 return result
1182
1183
1184__all__ = [
1185 "AnyMsg", "Bag", "ROSLogHandler", "FAMILY", "PARAM_SEPARATOR", "PRIVATE_PREFIX",
1186 "PY_LOG_LEVEL_TO_ROSPY_LEVEL", "ROS_ALIAS_TYPES", "ROS_TIME_CLASSES", "ROS_TIME_TYPES",
1187 "canonical", "create_client", "create_publisher", "create_rate", "create_service",
1188 "create_subscriber", "create_timer", "delete_param", "deserialize_message",
1189 "destroy_entity", "format_param_name", "get_logger", "get_message_class",
1190 "get_message_definition", "get_message_fields", "get_message_type",
1191 "get_message_type_hash", "get_message_value", "get_namespace", "get_node_name",
1192 "get_nodes", "get_param", "get_param_names", "get_params", "get_rostime",
1193 "get_service_definition", "get_service_request_class", "get_service_response_class",
1194 "get_services", "get_topics", "has_param", "init_node", "init_params", "is_ros_message",
1195 "is_ros_service", "is_ros_time", "make_duration", "make_time", "ok", "on_shutdown",
1196 "register_init", "remap_name", "resolve_name", "scalar", "serialize_message", "set_param",
1197 "shutdown", "sleep", "spin", "spin_once", "spin_until_future_complete", "start_spin",
1198 "to_duration", "to_nsec", "to_sec", "to_sec_nsec", "to_time", "wait_for_publisher",
1199 "wait_for_subscriber", "wait_for_service"
1200]
ROS1 bag reader and writer.
Definition ros1.py:126
dict __TYPEDEFS
Definition ros1.py:141
get_message_definition(self, msg_or_type)
Returns ROS1 message type definition full text from bag, including subtype definitions.
Definition ros1.py:170
dict __TYPES
Definition ros1.py:135
get_topic_info(self, *_, **__)
Returns topic and message type metainfo as {(topic, typename, typehash): count}.
Definition ros1.py:211
__init__(self, f, mode="r", compression=rosbag.Compression.NONE, chunk_threshold=768 *1024, allow_unindexed=False, options=None, skip_index=False)
Opens a ROS1 bag file.
Definition ros1.py:163
get_qoses(self, topic, typename)
Returns None (ROS2 bag API conformity stand-in).
Definition ros1.py:206
get_message_class(self, typename, typehash=None)
Returns ROS1 message class for typename, or None if unknown type.
Definition ros1.py:185
__ensure_typedef(self, typename, typehash=None)
Definition ros1.py:316
dict __PARSEDS
Definition ros1.py:147
get_message_type_hash(self, msg_or_type)
Returns ROS1 message type MD5 hash.
Definition ros1.py:195
__convert_message(self, msg, typename2, typehash2=None)
Definition ros1.py:287
__populate_meta(self)
Definition ros1.py:301
read_messages(self, topics=None, start_time=None, end_time=None, connection_filter=None, raw=False)
Yields messages from the bag in chronological order.
Definition ros1.py:239
write(self, topic, msg, t=None, raw=False, connection_header=None, **__)
Writes a message to the bag.
Definition ros1.py:280
reindex_file(f)
Reindexes bag file on disk.
Definition ros1.py:341
Container for local mutexes.
Definition ros1.py:368
Logging handler that forwards logging messages to ROS1 logger.
Definition ros1.py:85
emit(self, record)
Adds message to ROS1 logger.
Definition ros1.py:93
Logger wrapper with support for throttling logged messages per call site.
Definition util.py:38
is_ros_time(val)
Returns whether value is a ROS1 time/duration class or instance.
Definition ros1.py:1101
scalar(typename)
Returns scalar type from ROS message data type, like "uint8" from "uint8[100]".
Definition ros1.py:1134
make_time(secs=0, nsecs=0)
Returns a ROS1 time, as rospy.Time.
Definition ros1.py:1111
create_timer(period, callback, oneshot=False, immediate=False)
Returns a ROS1 timer instance.
Definition ros1.py:698
get_services(node=None, namespace=None, include_types=True)
Returns all available ROS1 services, as `[(service name, [type name, ]), ]`.
Definition ros1.py:757
create_service(service, cls_or_typename, callback)
Returns a ROS1 service server instance, for providing a service.
Definition ros1.py:642
to_duration(val)
Returns value as ROS1 duration if convertible (int/float/time/datetime/decimal), else value.
Definition ros1.py:1138
make_duration(secs=0, nsecs=0)
Returns a ROS1 duration, as rospy.Duration.
Definition ros1.py:1106
get_param_names()
Returns the names of all current ROS1 node parameters.
Definition ros1.py:485
get_message_class(msg_or_type)
Returns ROS1 message / service class object.
Definition ros1.py:939
is_ros_service(val)
Returns whether value is a ROS1 service class object or instance.
Definition ros1.py:1094
get_service_response_class(srv_or_type)
Returns ROS1 service response class object.
Definition ros1.py:1080
spin()
Spins ROS1 forever.
Definition ros1.py:565
get_logger()
Returns `logging.Logger` for logging to ROS1 log handler.
Definition ros1.py:781
register_init()
Informs `rosros` of ROS1 having been initialized outside of `init_node()`.
Definition ros1.py:416
create_rate(frequency)
Returns a ROS1 rate instance, for sleeping at a fixed rate.
Definition ros1.py:712
get_params(nested=True)
Returns the current ROS1 node parameters, by default as nested dictionary.
Definition ros1.py:499
create_client(service, cls_or_typename)
Returns a ROS1 service client instance, for invoking a service.
Definition ros1.py:630
deserialize_message(raw, cls_or_typename)
Returns ROS1 message or service request/response instantiated from serialized binary.
Definition ros1.py:1123
ok()
Returns whether ROS1 has been initialized and is not shut down.
Definition ros1.py:530
canonical(typename)
Returns "pkg/Type" for "pkg/subdir/Type".
Definition ros1.py:916
to_nsec(val)
Returns value in nanoseconds if value is ROS1 time/duration, else value.
Definition ros1.py:1152
get_message_type(msg_or_cls)
Returns ROS1 message / service canonical type name, like "std_msgs/Header".
Definition ros1.py:1010
get_message_header(val)
Returns message `std_msgs/Header`-attribute if any, else `None`.
Definition ros1.py:994
get_node_name()
Returns ROS1 node full name with namespace.
Definition ros1.py:731
to_sec(val)
Returns value in seconds if value is ROS1 time/duration, else value.
Definition ros1.py:1157
get_message_type_hash(msg_or_type)
Returns ROS1 message / service type MD5 hash.
Definition ros1.py:1024
get_topics()
Returns all available ROS1 topics, as `[(topic name, [type name, ]), ]`.
Definition ros1.py:742
create_subscriber(topic, cls_or_typename, callback, callback_args=None, queue_size=0, raw=False)
Returns a ROS1 subscriber instance.
Definition ros1.py:681
get_namespace()
Returns ROS1 node namespace.
Definition ros1.py:726
is_ros_message(val)
Returns whether value is a ROS1 message or service request/response class or instance.
Definition ros1.py:1090
spin_until_future_complete(future, timeout=None)
Spins ROS1 until future complete or timeout reached or ROS shut down.
Definition ros1.py:595
to_sec_nsec(val)
Returns value as (seconds, nanoseconds) if value is ROS1 time/duration, else value.
Definition ros1.py:1162
wait_for_publisher(topic, timeout=None, cls_or_typename=None)
Blocks until topic has at least one publisher.
Definition ros1.py:846
destroy_entity(item)
Closes the given publisher, subscriber, service client, or service server instance.
Definition ros1.py:716
get_service_request_class(srv_or_type)
Returns ROS1 service request class object.
Definition ros1.py:1068
get_message_definition(msg_or_type, full=True)
Returns ROS1 message or service request/response type definition text.
Definition ros1.py:970
serialize_message(msg)
Returns ROS1 message or service request/response as a serialized binary of `bytes()`.
Definition ros1.py:1116
wait_for_subscriber(topic, timeout=None, cls_or_typename=None)
Blocks until topic has at least one subscriber.
Definition ros1.py:870
get_service_definition(srv_or_type)
Returns ROS1 service type definition text.
Definition ros1.py:1053
create_publisher(topic, cls_or_typename, latch=False, queue_size=0)
Returns a ROS1 publisher instance.
Definition ros1.py:661
get_message_value(msg, name, default=...)
Returns object attribute value, with numeric arrays converted to lists.
Definition ros1.py:1034
spin_once(timeout=None)
Waits until timeout, or forever if timeout None or negative.
Definition ros1.py:585
get_message_fields(val)
Returns {field name: field type name} if ROS1 message or service request/response,...
Definition ros1.py:982
start_spin()
Sets ROS1 spinning forever in a background thread.
Definition ros1.py:551
get_rostime()
Returns current ROS1 time, as `rospy.Time`.
Definition ros1.py:785
get_nodes()
Returns all ROS1 nodes, as `[node full name, ]`.
Definition ros1.py:736
shutdown(reason=None)
Sends the shutdown signal to rospy.
Definition ros1.py:618
init_params(defaults=None, **defaultkws)
Declares all parameters on node from defaults dictionary.
Definition ros1.py:436
format_param_name(name)
Returns parameter name using "/" separator, and leading root or private sigils stripped.
Definition ros1.py:924
to_time(val)
Returns value as ROS1 time if convertible (int/float/duration/datetime/decimal), else value.
Definition ros1.py:1167