5------------------------------------------------------------------------------
6This file is part of rosros - simple unified interface to ROS1 / ROS2.
7Released under the BSD License.
12------------------------------------------------------------------------------
14## @namespace rosros.ros1
47## ROS1 time/duration types
48ROS_TIME_TYPES = ["time", "duration"]
50## ROS1 time/duration types mapped to type names
51ROS_TIME_CLASSES = {rospy.Time: "time", rospy.Duration: "duration"}
53## Mapping between ROS type aliases and real types, like {"byte": "int8"}
54ROS_ALIAS_TYPES = {"byte": "int8", "char": "uint8"}
56## Separator char between ROS1 parameter namespace parts
59## Prefix for "private" names, auto-namespaced under current namespace
62## ROS Python module family, "rospy"
65## Map rospy log level constants to Python logging level constants
66PY_LOG_LEVEL_TO_ROSPY_LEVEL = {
86 """Logging handler that forwards logging messages to ROS1 logger."""
90 self.
_logger = logging.getLogger(
"rosout")
94 """Adds message to ROS1 logger."""
95 if not self.
_logger.isEnabledFor(record.levelno):
98 text = record.msg % record.args
if record.args
else record.msg
102 text +=
"\n\n" +
"".join(traceback.format_exception(*record.exc_info))
103 self.
_logger.log(record.levelno, text)
107class Bag(rosbag.Bag):
109 ROS1 bag reader and writer.
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.
115 Does **
not** smooth over the rosbag bug of writing different types to one topic.
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).
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.
142 compression=rosbag.Compression.NONE, chunk_threshold=768 * 1024,
143 allow_unindexed=False, options=None, skip_index=False):
145 Opens a ROS1 bag file.
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
155 super().__init__(f, mode, compression, chunk_threshold,
156 allow_unindexed, options, skip_index)
162 """Returns ROS1 message type definition full text from bag, including subtype definitions."""
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)
171 Returns ROS1 message class for typename, or None if unknown type.
173 Generates
class dynamically if not already generated.
175 @param typehash message type definition hash,
if any
178 typehash = typehash or next((h
for n, h
in self.
__TYPEDEFS if n == typename),
None)
179 typekey = (typename, typehash)
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)
187 """Returns ROS1 message type MD5 hash."""
189 typename = msg_or_type
190 typehash = next((h
for n, h
in self.
__TYPEDEFS if n == typename),
None)
193 typehash = next((h
for n, h
in self.
__TYPEDEFS if n == typename),
None)
198 """Returns None (ROS2 bag API conformity stand-in)."""
203 """Returns topic and message type metainfo as {(topic, typename, typehash): count}."""
207 def read_messages(self, topics=None, start_time=None, end_time=None, connection_filter=None, raw=False):
209 Yields messages from the bag
in chronological order.
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
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}
232 kwargs = dict(topics=topics, start_time=
to_time(start_time), end_time=
to_time(end_time),
233 connection_filter=connection_filter, raw=raw)
236 yield rosbag.bag.BagMessage(topic, msg, stamp)
242 typename, typehash = (msg[0], msg[2])
if raw
else (msg._type, msg._md5sum)
243 if dupes[topic] != (typename, typehash):
248 yield rosbag.bag.BagMessage(topic, msg, stamp)
251 def write(self, topic, msg, t=None, raw=False, connection_header=None, **__):
253 Writes a message to the bag.
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"}
266 return super().
write(topic, msg,
to_time(t), raw, connection_header)
269 def __convert_message(self, msg, typename2, typehash2=None):
270 """Returns message converted to given type; fields must match."""
274 v1 = v2 = getattr(msg, fname)
275 if ftypename != fields2.get(fname, ftypename):
277 setattr(msg2, fname, v2)
281 def __populate_meta(self):
282 """Populates topics and message type definitions and hashes."""
283 result = collections.Counter()
284 counts = collections.Counter()
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
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)
299 for (roottype, roothash), rootdef
in list(self.
__TYPEDEFS.items()):
300 rootkey = (roottype, roothash)
303 subdefs = tuple(parsing.parse_definition_subtypes(rootdef).items())
304 subhashes = {n: parsing.calculate_definition_hash(n, d, 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())
317 Reindexes bag file on disk.
319 Makes a temporary copy in file directory.
321 with rosbag.Bag(f, allow_unindexed=
True, skip_index=
True)
as inbag:
322 inplace = (inbag.version > 102)
324 f2 = util.unique_path(
"%s.orig%s") % os.path.splitext(f)
326 inbag, outbag =
None,
None
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)
332 for _
in (outbag
if inplace
else inbag).reindex():
pass
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:
337 inbag
and inbag.close()
338 outbag
and outbag.close()
341 inbag
and inbag.close()
342 outbag
and outbag.close()
348 """Container for local mutexes."""
351 SPIN = threading.RLock()
354 SPIN_START = threading.RLock()
358def init_node(name, args=None, namespace=None, anonymous=False, log_level=None, enable_rosout=True):
360 Initializes rospy and creates ROS1 node.
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
371 global MASTER, logger
374 if namespace
and namespace !=
"/":
375 os.environ[
"ROS_NAMESPACE"] = namespace
376 rospy.names._set_caller_id(util.namejoin(namespace, name))
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)
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()
389 if not any(isinstance(x, ROSLogHandler)
for x
in logger.handlers):
391 if log_level
is not None:
392 logger.setLevel(log_level)
396 """Informs `rosros` of ROS1 having been initialized outside of `init_node()`."""
401 MASTER = rospy.client.get_master()
402 if not any(isinstance(x, ROSLogHandler)
for x
in logger.handlers):
408 Declares all parameters on node from defaults dictionary.
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
418 stack = list(util.merge_dicts(defaults or {}, defaultkws).items())
421 if isinstance(v, dict):
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())
427 if not rospy.has_param(k):
428 rospy.set_param(k, v)
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)
442 Returns whether the parameter exists.
444 @param name full name of the parameter
in node namespace
449def get_param(name, default=None, autoset=True):
451 Returns parameter value from current ROS1 node.
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
458 @throws KeyError
if parameter
not set
and default
not given
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)
467 """Returns the names of all current ROS1 node parameters."""
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):])
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}
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))
492def set_param(name, value):
494 Sets a parameter on the node.
496 @param name full name of the parameter
in node namespace
497 @param value parameter value to set
498 @return the set value
504def delete_param(name):
506 Deletes parameter from the node.
508 @param name full name of the parameter
in node namespace
509 @throws KeyError
if parameter
not set
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())
520def on_shutdown(callback, *args, **kwargs):
522 Registers function to be called on shutdown, after node has been torn down.
524 Function is called
with given arguments.
526 if args
or kwargs: callback = functools.partial(callback, *args, **kwargs)
527 rospy.on_shutdown(callback)
532 Sets ROS1 spinning forever in a background thread.
534 Does nothing
if spinning has already been started.
540 finally: SPINNER =
None
543 with Mutex.SPIN_START:
544 if SPINNER
or Mutex.SPIN._is_owned():
return
545 SPINNER = threading.Thread(target=do_spin)
550 """Spins ROS1 forever."""
555 except KeyboardInterrupt: SPINNER =
None
558 if Mutex.SPIN._is_owned():
559 with Mutex.SPIN:
return
566 Waits until timeout, or forever
if timeout
None or negative.
568 @param timeout time to sleep,
as seconds
or ROS duration;
569 None or <0 waits forever
571 rospy.rostime.wallsleep(2**31 - 1 if timeout
is None or timeout < 0
else to_sec(timeout))
576 Spins ROS1 until future complete or timeout reached
or ROS shut down.
578 @param future object
with `concurrent.futures.Future`-conforming interface to complete
579 @param timeout time to wait,
as seconds
or ROS1 duration
585 if not future.done():
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():
593 now = time.monotonic()
594 if not future.done()
and not ok():
595 future.cancel(
"ROS shut down")
600 Sends the shutdown signal to rospy.
602 @param reason shutdown reason to log,
if any
604 rospy.signal_shutdown(reason or "")
609 Returns a ROS1 service client instance, for invoking a service.
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
622 Returns a ROS1 service server instance, for providing a service.
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`
631 arity = util.get_arity(callback)
633 mycallback = functools.partial((
lambda f, c, r: f(r, c._response_class())), callback, cls)
635 mycallback = util.wrap_arity(callback)
if arity != 1
else callback
636 return rospy.Service(service, cls, mycallback)
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`
651 return rospy.Publisher(topic, cls, latch=latch, queue_size=queue_size
or 0)
655 queue_size=0, raw=False):
657 Returns a ROS1 subscriber instance.
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`
672 anymsg = issubclass(cls, rospy.AnyMsg) and not 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)
679def create_timer(period, callback, oneshot=False, immediate=False):
681 Returns a ROS1 timer instance.
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`
689 period, mycallback = make_duration(period), lambda *_, **__: callback()
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)
699 Returns a ROS1 rate instance, for sleeping at a fixed rate.
701 @param frequency rate to sleep at,
in Hz
704 return rospy.Rate(frequency)
708 """Closes the given publisher, subscriber, service client, or service server instance."""
709 if isinstance(item, rospy.topics.Topic):
711 elif isinstance(item, rospy.client.ServiceProxy):
713 elif callable(getattr(item,
"shutdown",
None)):
718 """Returns ROS1 node namespace."""
719 return rospy.get_namespace()
723 """Returns ROS1 node full name with namespace."""
724 return rospy.get_name()
728 """Returns all ROS1 nodes, as `[node full name, ]`."""
730 return sorted(set(l
for s
in MASTER.getSystemState()[-1]
for _, ll
in s
for l
in ll))
734 """Returns all available ROS1 topics, as `[(topic name, [type name, ]), ]`."""
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())
741def get_services(node=None, namespace=None, include_types=True):
743 Returns all available ROS1 services, as `[(service name, [type name, ]), ]`.
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
750 for name
in rosservice.get_service_list(node, namespace):
751 if not include_types:
752 result.append([name, []])
755 typename = rosservice.get_service_type(name)
756 if typename: result.append([name, [typename]])
758 logger.warning(
"Error retrieving service type for %s.", name, exc_info=
True)
759 return sorted(result)
764 Returns `logging.Logger` for logging to ROS1 log handler.
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.
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)
785def remap_name(name, namespace=None):
787 Returns the absolute remapped topic/service name if mapping exists.
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
794 return rospy.remap_name(name, namespace)
797def resolve_name(name, namespace=None):
799 Returns absolute remapped name, namespaced under current node if relative
or private.
801 @param namespace namespace to use
if not current node full name
805 return util.namesplit(namespace)[0] +
"/"
808 name2 = (
"/" if name.startswith(
"/")
else "") +
"/".join(filter(bool, name.split(
"/")))
809 if name2.startswith(
"~"):
810 name2 = util.namejoin(namespace, name2.lstrip(
"~"))
811 elif not name2.startswith(
"/"):
812 name2 = util.namejoin(util.namesplit(namespace)[0], name2)
814 return rospy.names.get_resolved_mappings().get(name2, name2)
819 Sleeps for the specified duration
in ROS time.
821 Raises error on ROS shutdown
or ROS time jumping backwards
823 @param duration time to sleep,
as seconds
or ROS duration, <=0 returns immediately
830 Blocks until topic has at least one publisher.
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
842 deadline =
None if timeout
is None or timeout < 0
else time.monotonic() + timeout
844 if "*" == typename: typename =
None
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
855 Blocks until topic has at least one subscriber.
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
867 deadline =
None if timeout
is None or timeout < 0
else time.monotonic() + timeout
869 if "*" == typename: typename =
None
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
878def wait_for_service(service, timeout=None, cls_or_typename=None):
880 Blocks until service is available.
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
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
894 fullname, first = resolve_name(service),
True
895 while not result
and (first
or deadline
is None or time.monotonic() < deadline):
897 try: rospy.wait_for_service(service, timeout)
898 except Exception:
continue
899 result =
not typename
902 result = typename
in srvs.get(fullname, [])
903 if not result
and fullname
in srvs: rospy.rostime.wallsleep(0.1)
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]
920 """Returns parameter name using "/" separator, and leading root or private sigils stripped."""
921 return name.replace(
".", PARAM_SEPARATOR).lstrip(
"~" + PARAM_SEPARATOR)
926 Returns ROS1 message / service class object.
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`,
937 return msg_or_type
if inspect.isclass(msg_or_type)
else type(msg_or_type)
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
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):
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
957 Returns ROS1 message or service request/response type definition text.
959 Text will include subtype definitions by default.
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
970 text = re.sub(
r"\n=+\n.+",
"", text, flags=re.DOTALL)
976 Returns {field name: field type name} if ROS1 message
or service request/response,
else {}.
978 @param val ROS1 message
or service request/response instance,
or class object
980 names = getattr(val, "__slots__", [])
981 if isinstance(val, tuple(ROS_TIME_CLASSES)):
982 names = genpy.TVal.__slots__
983 return dict(zip(names, getattr(val,
"_slot_types", [])))
988 Returns message `std_msgs/Header`-attribute if any,
else `
None`.
990 @param val ROS message
or service request/response instance
994 if "std_msgs/Header" == typename:
1001 Returns ROS1 message / service canonical type name, like "std_msgs/Header".
1003 Returns
"*" for `AnyMsg`.
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
1010 cls = msg_or_cls
if inspect.isclass(msg_or_cls)
else type(msg_or_cls)
1011 return "%s/%s" % (FAMILY, cls.__name__)
1017 Returns ROS1 message / service type MD5 hash.
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`
1029 Returns object attribute value, with numeric arrays converted to lists.
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
1034 try: v, parent, k = util.get_nested(msg, name)
1036 if default
is not Ellipsis:
return default
1046 Returns ROS1 service type definition text.
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
1055 reqcls, respcls = cls._request_class, cls._response_class
1057 return "%s\n---\n%s" % (reqtxt, resptxt)
1062 Returns ROS1 service request class object.
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`
1075 Returns ROS1 service response class object.
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`
1088 Returns whether value is a ROS1 message
or service request/response
class or instance.
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,
1094 return (issubclass
if inspect.isclass(val)
else isinstance)(val, (genpy.Message, genpy.TVal))
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 \
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)
1110 """Returns a ROS1 duration, as rospy.Duration."""
1111 return rospy.Duration(secs=secs, nsecs=nsecs)
1115 """Returns a ROS1 time, as rospy.Time."""
1116 return rospy.Time(secs=secs, nsecs=nsecs)
1120 """Returns ROS1 message or service request/response as a serialized binary of `bytes()`."""
1123 return buf.getvalue()
1127 """Returns ROS1 message or service request/response instantiated from serialized binary."""
1134 Returns scalar type from ROS message data type, like
"uint8" from "uint8[100]".
1136 Returns type unchanged
if already a scalar.
1138 return typename[:typename.index(
"[")]
if "[" in typename
else typename
1142 """Returns value as ROS1 duration if convertible (int/float/time/datetime/decimal), else value."""
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)
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
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
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
1171 """Returns value as ROS1 time if convertible (int/float/duration/datetime/decimal), else value."""
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)
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"
ROS1 bag reader and writer.
get_message_definition(self, msg_or_type)
Returns ROS1 message type definition full text from bag, including subtype definitions.
get_topic_info(self, *_, **__)
Returns topic and message type metainfo as {(topic, typename, typehash): count}.
__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.
get_qoses(self, topic, typename)
Returns None (ROS2 bag API conformity stand-in).
get_message_class(self, typename, typehash=None)
Returns ROS1 message class for typename, or None if unknown type.
__ensure_typedef(self, typename, typehash=None)
get_message_type_hash(self, msg_or_type)
Returns ROS1 message type MD5 hash.
__convert_message(self, msg, typename2, typehash2=None)
read_messages(self, topics=None, start_time=None, end_time=None, connection_filter=None, raw=False)
Yields messages from the bag in chronological order.
write(self, topic, msg, t=None, raw=False, connection_header=None, **__)
Writes a message to the bag.
reindex_file(f)
Reindexes bag file on disk.
Container for local mutexes.
Logging handler that forwards logging messages to ROS1 logger.
emit(self, record)
Adds message to ROS1 logger.
Logger wrapper with support for throttling logged messages per call site.
is_ros_time(val)
Returns whether value is a ROS1 time/duration class or instance.
scalar(typename)
Returns scalar type from ROS message data type, like "uint8" from "uint8[100]".
make_time(secs=0, nsecs=0)
Returns a ROS1 time, as rospy.Time.
create_timer(period, callback, oneshot=False, immediate=False)
Returns a ROS1 timer instance.
get_services(node=None, namespace=None, include_types=True)
Returns all available ROS1 services, as `[(service name, [type name, ]), ]`.
create_service(service, cls_or_typename, callback)
Returns a ROS1 service server instance, for providing a service.
to_duration(val)
Returns value as ROS1 duration if convertible (int/float/time/datetime/decimal), else value.
make_duration(secs=0, nsecs=0)
Returns a ROS1 duration, as rospy.Duration.
get_param_names()
Returns the names of all current ROS1 node parameters.
get_message_class(msg_or_type)
Returns ROS1 message / service class object.
is_ros_service(val)
Returns whether value is a ROS1 service class object or instance.
get_service_response_class(srv_or_type)
Returns ROS1 service response class object.
spin()
Spins ROS1 forever.
get_logger()
Returns `logging.Logger` for logging to ROS1 log handler.
register_init()
Informs `rosros` of ROS1 having been initialized outside of `init_node()`.
create_rate(frequency)
Returns a ROS1 rate instance, for sleeping at a fixed rate.
get_params(nested=True)
Returns the current ROS1 node parameters, by default as nested dictionary.
create_client(service, cls_or_typename)
Returns a ROS1 service client instance, for invoking a service.
deserialize_message(raw, cls_or_typename)
Returns ROS1 message or service request/response instantiated from serialized binary.
ok()
Returns whether ROS1 has been initialized and is not shut down.
canonical(typename)
Returns "pkg/Type" for "pkg/subdir/Type".
to_nsec(val)
Returns value in nanoseconds if value is ROS1 time/duration, else value.
get_message_type(msg_or_cls)
Returns ROS1 message / service canonical type name, like "std_msgs/Header".
get_message_header(val)
Returns message `std_msgs/Header`-attribute if any, else `None`.
get_node_name()
Returns ROS1 node full name with namespace.
to_sec(val)
Returns value in seconds if value is ROS1 time/duration, else value.
get_message_type_hash(msg_or_type)
Returns ROS1 message / service type MD5 hash.
get_topics()
Returns all available ROS1 topics, as `[(topic name, [type name, ]), ]`.
create_subscriber(topic, cls_or_typename, callback, callback_args=None, queue_size=0, raw=False)
Returns a ROS1 subscriber instance.
get_namespace()
Returns ROS1 node namespace.
is_ros_message(val)
Returns whether value is a ROS1 message or service request/response class or instance.
spin_until_future_complete(future, timeout=None)
Spins ROS1 until future complete or timeout reached or ROS shut down.
to_sec_nsec(val)
Returns value as (seconds, nanoseconds) if value is ROS1 time/duration, else value.
wait_for_publisher(topic, timeout=None, cls_or_typename=None)
Blocks until topic has at least one publisher.
destroy_entity(item)
Closes the given publisher, subscriber, service client, or service server instance.
get_service_request_class(srv_or_type)
Returns ROS1 service request class object.
get_message_definition(msg_or_type, full=True)
Returns ROS1 message or service request/response type definition text.
serialize_message(msg)
Returns ROS1 message or service request/response as a serialized binary of `bytes()`.
wait_for_subscriber(topic, timeout=None, cls_or_typename=None)
Blocks until topic has at least one subscriber.
get_service_definition(srv_or_type)
Returns ROS1 service type definition text.
create_publisher(topic, cls_or_typename, latch=False, queue_size=0)
Returns a ROS1 publisher instance.
get_message_value(msg, name, default=...)
Returns object attribute value, with numeric arrays converted to lists.
spin_once(timeout=None)
Waits until timeout, or forever if timeout None or negative.
get_message_fields(val)
Returns {field name: field type name} if ROS1 message or service request/response,...
start_spin()
Sets ROS1 spinning forever in a background thread.
get_rostime()
Returns current ROS1 time, as `rospy.Time`.
get_nodes()
Returns all ROS1 nodes, as `[node full name, ]`.
shutdown(reason=None)
Sends the shutdown signal to rospy.
init_params(defaults=None, **defaultkws)
Declares all parameters on node from defaults dictionary.
format_param_name(name)
Returns parameter name using "/" separator, and leading root or private sigils stripped.
to_time(val)
Returns value as ROS1 time if convertible (int/float/duration/datetime/decimal), else value.