rosros 0.2.5
Simple unified interface to ROS1 / ROS2 Python API
Loading...
Searching...
No Matches
patch.py
Go to the documentation of this file.
1"""
2Patches ROS1 classes with ROS2-compatible members, and vice versa.
3
4------------------------------------------------------------------------------
5This file is part of rosros - simple unified interface to ROS1 / ROS2.
6Released under the BSD License.
7
8@author Erki Suurjaak
9@created 23.02.2022
10@modified 07.12.2023
11------------------------------------------------------------------------------
12"""
13## @namespace rosros.patch
14import functools
15import numbers
16import os
17import threading
18import time
19import traceback
20
21
22rospy = rclpy = ros = None
23if os.getenv("ROS_VERSION") != "2":
24 import genpy
25 import rospy
26
27 from . rclify.clock import Clock, ClockType
28 from . import ros1 as ros
29else:
30 import rclpy.duration
31 import rclpy.node
32 import rclpy.publisher
33 import rclpy.service
34 import rclpy.subscription
35 import rclpy.time
36 import rclpy.timer
37 from rclpy.clock import ClockType
38
39 from . import ros2 as ros
40 from . rospify import AnyMsg
41
42from . import api
43from . import util
44
45
46
47EXTRA_ATTRS = {}
48
49
50PATCHED = False
51
52
53PATCHED_FULL = False
54
55
57 """Patches ROS1 classes with generic compatibility with ROS2 and rosros."""
58 global PATCHED
59 if PATCHED: return
60
61 rospy.Publisher.publish = Publisher__publish
62
63 rospy.Service.__init__ = Service__init
64
65 rospy.ServiceProxy.call = ServiceProxy__call
66 rospy.ServiceProxy.call_async = ServiceProxy__call_async
67 rospy.ServiceProxy.wait_for_service = ServiceProxy__wait_for_service
68 rospy.ServiceProxy.service_is_ready = ServiceProxy__service_is_ready
69
70 rospy.Subscriber.__init__ = Subscriber__init
71 rospy.topics._SubscriberImpl.receive_callback = SubscriberImpl__receive_callback
72
73 PATCHED = True
74
75
77 """Patches ROS1 classes with full stand-ins for ROS2 methods and properties."""
78 global PATCHED_FULL
80 if PATCHED_FULL: return
81
82 rospy.ServiceProxy.destroy = rospy.ServiceProxy.close
83 rospy.ServiceProxy.remove_pending_request = ServiceProxy__remove_pending_request
84
85 rospy.Service.destroy = rospy.Service.shutdown
86
87 rospy.Publisher.topic_name = property(lambda self: self.name)
88 rospy.Publisher.assert_liveliness = Publisher__assert_liveliness
89 rospy.Publisher.destroy = rospy.Publisher.unregister
90 rospy.Publisher.get_subscription_count = rospy.Publisher.get_num_connections
91
92 rospy.Subscriber.topic_name = property(lambda self: self.name)
93 rospy.Subscriber.destroy = rospy.Subscriber.unregister
94
95 rospy.Time.__init__ = Time__init
96 rospy.Time.__del__ = Time__del
97 rospy.Time.nanoseconds = property(rospy.Duration.to_nsec)
98 rospy.Time.clock_type = property(Time__clock_type)
99 rospy.Time.seconds_nanoseconds = Time__seconds_nanoseconds
100 rospy.Time.to_msg = Temporal__to_msg
101 rospy.Time.from_msg = Time__from_msg
102
103 rospy.Duration.__init__ = Duration__init
104 rospy.Duration.nanoseconds = property(rospy.Duration.to_nsec)
105 rospy.Duration.to_msg = Temporal__to_msg
106 rospy.Duration.from_msg = Duration__from_msg
107
108 rospy.Rate.destroy = lambda self: None
109
110 rospy.Timer.__init__ = Timer__init
111 rospy.Timer.run = Timer__run
112 rospy.Timer.cancel = Timer__cancel
113 rospy.Timer.reset = Timer__reset
114 rospy.Timer.shutdown = Timer__shutdown
115 rospy.Timer.destroy = Timer__shutdown
116 rospy.Timer.is_canceled = Timer__is_canceled
117 rospy.Timer.is_ready = Timer__is_ready
118 rospy.Timer.time_since_last_call = Timer__time_since_last_call
119 rospy.Timer.time_until_next_call = Timer__time_until_next_call
120 rospy.Timer.clock = property(Timer__clock)
121 rospy.Timer.timer_period_ns = property(Timer__timer_period_ns,
122 Timer__timer_period_ns__setter)
123
124 PATCHED_FULL = True
125
126
128 """Patches ROS2 classes with full stand-ins for ROS1 methods and properties."""
129 global PATCHED, PATCHED_FULL
130 if PATCHED: return
131
132 rclpy.client.Client.call = client_call_wrapper(rclpy.client.Client.call)
133 rclpy.client.Client.call_async = client_call_wrapper(rclpy.client.Client.call_async)
134 rclpy.client.Client.__call__ = rclpy.client.Client.call
135 rclpy.client.Client.close = rclpy.client.Client.destroy
136 rclpy.client.Client.service_class = property(lambda self: self.srv_type)
137 rclpy.client.Client.request_class = property(lambda self: self.srv_type.Request)
138 rclpy.client.Client.response_class = property(lambda self: self.srv_type.Response)
139 rclpy.client.Client.resolved_name = property(lambda self: self.srv_name)
140
141 rclpy.publisher.Publisher.publish = publish_wrapper(rclpy.publisher.Publisher.publish)
142 rclpy.publisher.Publisher.get_num_connections = rclpy.publisher.Publisher.get_subscription_count
143 rclpy.publisher.Publisher.unregister = rclpy.publisher.Publisher.destroy
144 rclpy.publisher.Publisher.resolved_name = rclpy.publisher.Publisher.topic_name
145 rclpy.publisher.Publisher.name = property(lambda self: self.topic)
146 rclpy.publisher.Publisher.data_class = property(lambda self: self.msg_type)
147 rclpy.publisher.Publisher.type = property(lambda self: ros.get_message_type(self.msg_type))
148 rclpy.publisher.Publisher.md5sum = property(lambda self: ros.get_message_type_hash(self.msg_type))
149
150 rclpy.service.Service.__init__ = Service__init
151 rclpy.service.Service.spin = Service__spin
152 rclpy.service.Service.shutdown = rclpy.service.Service.destroy
153 rclpy.service.Service.service_class = property(lambda self: self.srv_type)
154 rclpy.service.Service.request_class = property(lambda self: self.srv_type.Request)
155 rclpy.service.Service.response_class = property(lambda self: self.srv_type.Response)
156 rclpy.service.Service.resolved_name = property(lambda self: self.srv_name)
157
158 rclpy.subscription.Subscription.__init__ = Subscription__init
159 rclpy.subscription.Subscription.get_num_connections = lambda self: 0 # Data not available in ROS2
160 rclpy.subscription.Subscription.unregister = rclpy.subscription.Subscription.destroy
161 rclpy.subscription.Subscription.resolved_name = rclpy.subscription.Subscription.topic_name
162 rclpy.subscription.Subscription.name = property(lambda self: self.topic)
163 rclpy.subscription.Subscription.data_class = property(lambda self: self.msg_type)
164 rclpy.subscription.Subscription.type = property(lambda self: ros.get_message_type(self.msg_type))
165 rclpy.subscription.Subscription.md5sum = property(lambda self: ros.get_message_type_hash(self.msg_type))
166 rclpy.subscription.Subscription.callback_args = property(Subscription__callback_args)
167 rclpy.subscription.Subscription._invoke_callbacks = Subscription__invoke_callbacks
168
169 rclpy.duration.Duration.__init__ = Duration__init
170 rclpy.duration.Duration.__abs__ = Duration__abs
171 rclpy.duration.Duration.__neg__ = Duration__neg
172 rclpy.duration.Duration.__add__ = Duration__add
173 rclpy.duration.Duration.__radd__ = Duration__add
174 rclpy.duration.Duration.__sub__ = Duration__sub
175 rclpy.duration.Duration.__mul__ = Duration__mul
176 rclpy.duration.Duration.__rmul__ = Duration__mul
177 rclpy.duration.Duration.__floordiv__ = Duration__floordiv
178 rclpy.duration.Duration.__truediv__ = Duration__truediv
179 rclpy.duration.Duration.__mod__ = Duration__mod
180 rclpy.duration.Duration.__divmod__ = Duration__divmod
181 rclpy.duration.Duration.__bool__ = TVal__is_nonzero
182 rclpy.duration.Duration.is_zero = TVal__is_zero
183 rclpy.duration.Duration.to_sec = TVal__to_sec
184 rclpy.duration.Duration.to_nsec = TVal__to_nsec
185 rclpy.duration.Duration.secs = property(TVal__secs)
186 rclpy.duration.Duration.nsecs = property(TVal__nsecs)
187
188 rclpy.time.Time.__init__ = Time__init
189 rclpy.time.Time.__bool__ = TVal__is_nonzero
190 rclpy.time.Time.is_zero = TVal__is_zero
191 rclpy.time.Time.to_sec = TVal__to_sec
192 rclpy.time.Time.to_nsec = TVal__to_nsec
193 rclpy.time.Time.secs = property(TVal__secs)
194 rclpy.time.Time.nsecs = property(TVal__nsecs)
195
196 rclpy.timer.Rate.remaining = Rate__remaining
197
198 rclpy.timer.Timer.shutdown = rclpy.timer.Timer.destroy
199 rclpy.timer.Timer.ident = property(Timer__ident)
200 rclpy.timer.Timer.native_id = property(Timer__ident)
201 rclpy.timer.Timer.getName = Timer__getName
202 rclpy.timer.Timer.setName = Timer__setName
203 rclpy.timer.Timer.name = property(Timer__getName, Timer__setName)
204 rclpy.timer.Timer.isDaemon = Timer__isDaemon
205 rclpy.timer.Timer.setDaemon = Timer__setDaemon
206 rclpy.timer.Timer.daemon = property(Timer__isDaemon, Timer__setDaemon)
207 rclpy.timer.Timer.join = Timer__join
208 rclpy.timer.Timer.is_alive = Timer__is_alive
209 rclpy.timer.Timer.isAlive = Timer__is_alive
210
211
212 PATCHED = PATCHED_FULL = True
213
214
215def set_extra_attribute(obj, name, value):
216 """Sets value for object patched attribute."""
217 if hasattr(obj, name):
218 setattr(obj, name, value)
219 else:
220 EXTRA_ATTRS.setdefault(id(obj), {})[name] = value
221
222
223if rospy: # Patch-functions to apply on ROS1 classes, to achieve parity with ROS2 API
224
225 def service_serve_wrapper(self, serve):
226 """Returns service serve-function wrapped to ensure return with response instance."""
227 def inner(req):
228 resp = serve(req)
229 return {} if resp is None else resp
230 return functools.update_wrapper(inner, serve)
231
232
234 """Does nothing (ROS2 compatibility stand-in)."""
235
236
237 ROS1_Publisher__publish = rospy.Publisher.publish
238
239 def Publisher__publish(self, *args, **kwds):
240 """Publishes message, supports giving message field values as dictionary in args[0]."""
241 attributes = functools.partial(ros.get_message_fields, self.data_class)
242 msg = util.ensure_object(self.data_class, attributes, api.dict_to_message, *args, **kwds)
243 return ROS1_Publisher__publish(self, msg)
244
245
246 ROS1_Service__init = rospy.Service.__init__
247
248 def Service__init(self, name, service_class, handler, buff_size=65536, error_handler=None):
249 """Wraps Service.__init__() to support returning None from server callback."""
250 callback = service_serve_wrapper(self, handler)
251 ROS1_Service__init(self, name, service_class, callback, buff_size, error_handler)
252
253
254 ROS1_ServiceProxy__call = rospy.ServiceProxy.call
255
256 def ServiceProxy__call(self, *args, **kwargs):
257 """Calls the service, supports giving request field values as dictionary in args[0]."""
258 reqcls = self.request_class
259 attributes = functools.partial(ros.get_message_fields, reqcls)
260 req = util.ensure_object(reqcls, attributes, api.dict_to_message, *args, **kwargs)
261 return ROS1_ServiceProxy__call(self, req)
262
263 def ServiceProxy__call_async(self, *args, **kwargs):
264 """
265 Make a service request and asyncronously get the result.
266
267 Service is invoked in a background thread.
268
269 This accepts either a request message instance,
270 or positional and keyword arguments to create a new request instance.
271
272 @return `concurrent.futures.Future`-conforming interface that completes when request does
273 """
274 reqcls = self.request_class
275 attributes = functools.partial(ros.get_message_fields, reqcls)
276 req = util.ensure_object(reqcls, attributes, api.dict_to_message, *args, **kwargs)
277 return util.start_future(self.call, req)
278
279 def ServiceProxy__remove_pending_request(self, future):
280 """
281 Does nothing (ROS2 compatibility stand-in).
282
283 @param future ignored (ROS2 compatibility stand-in)
284 """
285
287 """
288 Returns whether service is ready.
289
290 Provides rospy.ServiceProxy with service_is_ready() like rclpy.client.Client has.
291
292 @return True if a server is ready, False otherwise
293 """
294 # Heuristically chosen 1ms; rospy might not check service at all if timeout too short
295 return self.wait_for_service(1E-3)
296
297 def ServiceProxy__wait_for_service(self, timeout=None, timeout_sec=None):
298 """
299 Waits for a service server to become ready.
300
301 Returns as soon as a server becomes ready or if the timeout expires.
302
303 Gives rospy.ServiceProxy.wait_for_service the behavior of
304 rclpy.client.Client.wait_for_service.
305
306 @param timeout time to wait, as seconds or ROS Duration. If None, then wait forever.
307 @param timeout_sec seconds to wait; if None, wait forever (ROS2 compatibility stand-in)
308 @return True if server became ready or False on a timeout
309 """
310 if timeout_sec is not None: timeout = timeout_sec
311 try:
312 rospy.wait_for_service(self.resolved_name, ros.to_nsec(timeout))
313 except rospy.ROSInterruptException: raise
314 except rospy.ROSException:
315 return False
316 return True
318 ROS1_Subscriber__init = rospy.Subscriber.__init__
319 ROS1_SubscriberImpl__receive_callback = rospy.topics._SubscriberImpl.receive_callback
321 def Subscriber__init(self, name, data_class, callback=None, callback_args=None,
322 queue_size=None, buff_size=65536, tcp_nodelay=False):
323 """Wraps rospy.Subscriber.__init__() to add raw bytes autoconversion support."""
324 ROS1_Subscriber__init(self, name, data_class, callback, callback_args,
325 queue_size, buff_size, tcp_nodelay)
326 self._anymsg = issubclass(data_class, rospy.AnyMsg) # Whether to return AnyMsg or bytes
327 self.impl._interface = self
329 def SubscriberImpl__receive_callback(self, msgs, connection):
330 """Wraps rospy.topics.SubscriberImpl.receive_callback() with raw bytes autoconversion."""
331 if not self._interface._anymsg and issubclass(self.data_class, rospy.AnyMsg):
332 msgs = [x._buff for x in msgs]
333 ROS1_SubscriberImpl__receive_callback(self, msgs, connection)
334
336 ROS1_Duration__init = rospy.Duration.__init__
338 def Duration__init(self, secs=0, nsecs=0, *, seconds=0, nanoseconds=0):
339 """Constructs a new `Duration`, using either ROS1 or ROS2 arguments."""
340 if secs or nsecs: seconds, nanoseconds = secs, nsecs
341 ROS1_Duration__init(self, seconds, nanoseconds)
343 def Temporal__to_msg(self):
344 """Returns a new Time or Duration from this."""
345 return type(self)(self.secs, self.nsecs)
346
347 @classmethod
348 def Duration__from_msg(cls, msg):
349 """Returns a new Duration from given."""
350 return cls(msg.secs, msg.nsecs)
351
353 ROS1_Time__init = rospy.Time.__init__
355 def Time__init(self, secs=0, nsecs=0, *, seconds=0, nanoseconds=0,
356 clock_type=ClockType.SYSTEM_TIME):
357 """Constructs a new ROS1 `Time`, using either ROS1 or ROS2 arguments."""
358 if secs or nsecs: seconds, nanoseconds = secs, nsecs
359 ROS1_Time__init(self, seconds, nanoseconds)
360 # Keep extra attributes externally, as Time attributes are set in __slots__
361 if clock_type != ClockType.SYSTEM_TIME:
362 EXTRA_ATTRS.setdefault(id(self), {})["_clock_type"] = clock_type
364 def Time__del(self):
365 """Clear object-related data, if any."""
366 EXTRA_ATTRS.pop(id(self), None)
369 """Returns time as (seconds, nanoseconds)."""
370 return (self.secs, self.nsecs)
372 def Time__clock_type(self):
373 """Returns the ClockType of this Time."""
374 attrs = EXTRA_ATTRS.get(id(self)) or {}
375 return attrs.get("_clock_type", ClockType.SYSTEM_TIME)
376
377 @classmethod
378 def Time__from_msg(cls, msg, clock_type=ClockType.ROS_TIME):
379 """Returns a new Time from given."""
380 return cls(msg.secs, msg.nsecs, clock_type=clock_type)
381
383 ROS1_Timer__init = rospy.Timer.__init__
384
385 def Timer__init(self, period, callback, oneshot=False, reset=False):
386 """
387 Constructs a new ROS1 Timer.
388
389 Plugs in ROS2 API compatibility: cancelling, resetting, and public timing info.
390
391 @param period desired period between callbacks, as rospy.Duration
392 @param callback callback to be called, taking rospy.TimerEvent
393 @param oneshot if True, fire only once, otherwise fire continuously
394 until shutdown is called [default: False]
395 @param reset if True, timer is reset when rostime moved backward. [default: False]
396 """
397 self._paused = False
398 self._wakeable = threading.Event() # Interruptable sleeper
399 self._time_last = None # rospy.Time() of start or last call or reset
400 self._time_next = None # rospy.Time() of next expected call
401 self._clock = Clock()
402 ROS1_Timer__init(self, period, callback, oneshot, reset) # Starts thread
404 def Timer__shutdown(self):
405 """Stop firing callbacks."""
406 self._shutdown = True
407 self._wakeable.set()
409 def Timer__run(self):
410 """Override rospy.Timer.run() to support canceling and resetting, and call timestamps."""
411 self._time_last = self._clock.now()
412 self._time_next = self._time_last + self._period
413 last_expected, last_real, last_duration = None, None, None
414 self._wakeable.clear()
415 while not rospy.core.is_shutdown() and not self._shutdown:
416 try:
417 rostimer_sleep_on_event(self._period.to_sec(), event=self._wakeable)
418 except rospy.exceptions.ROSTimeMovedBackwardsException:
419 if not self._reset:
420 raise
421 except rospy.exceptions.ROSInterruptException:
422 if rospy.core.is_shutdown():
423 break
424 raise
425 if self._shutdown:
426 break
427 if self._paused:
428 self._wakeable.wait()
429 if self._shutdown:
430 break
431 if self._paused is None:
432 self._paused = False
433 self._time_last = self._clock.now()
434 self._time_next = self._time_last + self._period
435 last_expected, last_real, last_duration = None, None, None
436 self._wakeable.clear()
437 continue
438 current_real = self._clock.now()
439 start = time.time()
440 self._time_last = self._clock.now()
441 self._callback(rospy.timer.TimerEvent(last_expected, last_real, self._time_next,
442 current_real, last_duration))
443 if self._oneshot:
444 break
445 last_duration = time.time() - start
446 last_expected, last_real = self._time_next, current_real
447 self._time_next += self._period
449 def Timer__cancel(self):
450 """Sets timer on pause, until reset or shutdown."""
451 if self._paused: return
452
453 self._paused = True
454 self._wakeable.set()
456 def Timer__reset(self):
457 """Sets last call time to now, postponing next callback by full period; uncancels if canceled."""
458 self._paused = None
459 self._wakeable.set()
461 def Timer__clock(self):
462 """Returns the Clock used by this timer."""
463 return self._clock
465 def Timer__timer_period_ns(self):
466 """Returns timer period in nanoseconds."""
467 return self._period.to_nsec()
469 def Timer__timer_period_ns__setter(self, value):
470 """Sets timer period in nanoseconds, resets timer if running."""
471 self._period = rospy.Duration(nsecs=value)
472 if not self._paused: self._wakeable.set()
474 def Timer__is_canceled(self):
475 """Returns whether the timer has been canceled (paused until reset or shutdown)."""
476 return bool(self._paused)
478 def Timer__is_ready(self):
479 """Returns whether timer callback should be called right now."""
480 if self._paused or self._time_last is None:
481 return False
482 return self._period >= (self._time_next - self._time_last)
485 """Returns nanoseconds since last timer callback (ROS2 compatibility stand-in)."""
486 return None if self._time_last is None else (self._clock.now() - self._time_last).to_nsec()
489 """Returns nanoseconds until next timer callback (ROS2 compatibility stand-in)."""
490 if self._shutdown or self._paused or self._time_next is None:
491 return None
492 return (self._time_next - self._clock.now()).to_nsec()
493
494
495
496 def rostimer_sleep_on_event(duration, event=None):
497 """
498 Sleeps for the specified duration in ROS time, or until event gets set.
499
500 Returns immediately if duration is negative or event is set.
501
502 @param duration seconds (or rospy.Duration) to sleep
503 @param event threading.Event to wait() on, if any,
504 will return early if event gets set
505
506 @throws ROSInterruptException if ROS shutdown occurs before sleep completes
507 @throws ROSTimeMovedBackwardsException if ROS time is set backwards
508 """
509 if event and event.is_set(): return
510
511 if rospy.rostime.is_wallclock():
512 if isinstance(duration, genpy.Duration):
513 duration = duration.to_sec()
514 if duration < 0:
515 return
516 event.wait(duration) if event else rospy.rostime.wallsleep(duration)
517 else:
518 initial_rostime = rospy.rostime.get_rostime()
519 if not isinstance(duration, genpy.Duration):
520 duration = genpy.Duration.from_sec(duration)
521
522 rostime_cond = rospy.rostime.get_rostime_cond()
523
524 if initial_rostime == genpy.Time(0):
525 # break loop if time is initialized or node is shutdown
526 while initial_rostime == genpy.Time(0) and \
527 not rospy.core.is_shutdown():
528 with rostime_cond:
529 rostime_cond.wait(0.3)
530 initial_rostime = rospy.rostime.get_rostime()
531 if event and event.is_set(): return
532
533 sleep_t = initial_rostime + duration
534
535 # break loop if sleep_t is reached, time moves backwards, or node is shutdown
536 while rospy.rostime.get_rostime() < sleep_t \
537 and rospy.rostime.get_rostime() >= initial_rostime and not rospy.core.is_shutdown():
538 with rostime_cond:
539 rostime_cond.wait(0.5)
540 if event and event.is_set(): return
541
542 if rospy.rostime.get_rostime() < initial_rostime:
543 time_jump = (initial_rostime - rospy.rostime.get_rostime()).to_sec()
544 raise rospy.exceptions.ROSTimeMovedBackwardsException(time_jump)
545 if rospy.core.is_shutdown():
546 raise rospy.exceptions.ROSInterruptException("ROS shutdown request")
547
548
549
550elif rclpy: # Patch-functions to apply on ROS2 classes, to achieve parity with ROS1 API
551
552 def client_call_wrapper(call):
553 """Returns service client call-function wrapped to ensure invoke with request instance."""
554 def inner(self, *args, **kwargs):
555 reqcls = self.srv_type.Request
556 attributes = functools.partial(ros.get_message_fields, reqcls)
557 req = util.ensure_object(reqcls, attributes, api.dict_to_message, *args, **kwargs)
558 return call(self, req)
559 return functools.update_wrapper(inner, call)
560
561 def service_serve_wrapper(self, serve):
562 """Returns service serve-function wrapped to ensure return with response instance."""
563 def inner(req, resp):
564 resp0, respcls, args, kwargs = resp, self.srv_type.Response, None, None
565 resp = serve(req, resp0)
566 if isinstance(resp, (list, tuple)): args = resp
567 elif isinstance(resp, dict): kwargs = resp
568 elif resp is None: resp = resp0
569 if args is not None or kwargs is not None:
570 attributes = functools.partial(ros.get_message_fields, respcls)
571 args, kwargs = (args or []), (kwargs or {})
572 resp = util.ensure_object(respcls, attributes, api.dict_to_message, *args, **kwargs)
573 return resp
574 return functools.update_wrapper(inner, serve)
575
576 def publish_wrapper(publish):
577 """Returns publish-function wrapped to ensure invoke with message instance."""
578 def inner(self, *args, **kwargs):
579 attrs = functools.partial(ros.get_message_fields, self.msg_type)
580 msg = util.ensure_object(self.msg_type, attrs, api.dict_to_message, *args, **kwargs)
581 return publish(self, msg)
582 return functools.update_wrapper(inner, publish)
583
585
586 ROS2_Duration__init = rclpy.duration.Duration.__init__
587
588 def Duration__init(self, secs=0, nsecs=0, *, seconds=0, nanoseconds=0):
589 """Constructs a new ROS2 `Duration`, using either ROS1 or ROS2 arguments."""
590 if secs or nsecs: seconds, nanoseconds = secs, nsecs
591 ROS2_Duration__init(self, seconds=seconds, nanoseconds=nanoseconds)
592
593 def Duration__neg(self):
594 """Returns negative value of this duration."""
595 return self.__class__(nanoseconds=-self.nanoseconds)
596
597 def Duration__abs(self):
598 """Returns absolute value of this duration."""
599 if self.nanoseconds >= 0:
600 return self
601 return self.__neg__()
602
603 def Duration__add(self, other):
604 """
605 Returns a new duration, adding given duration to this.
607 @param other another `Duration` instance
608 @returns new `Duration` instance
609 """
610 if isinstance(other, rclpy.duration.Duration):
611 return self.__class__(nanoseconds=self.nanoseconds + other.nanoseconds)
612 return NotImplemented
613
614 def Duration__sub(self, other):
615 """
616 Subtracts a duration from this duration.
617
618 @param other another `Duration` instance
619 @returns new `Duration` instance
620 """
621 if isinstance(other, rclpy.duration.Duration):
622 return self.__class__(nanoseconds=self.nanoseconds - other.nanoseconds)
623 return NotImplemented
624
625 def Duration__mul(self, val):
626 """
627 Multiplies this duration by an integer or float.
628
629 @param val multiplication factor, as `int` or `float`
630 @return new `Duration` instance
631 """
632 if isinstance(val, numbers.Number):
633 return self.__class__(nanoseconds=self.nanoseconds * val)
634 return NotImplemented
635
636 def Duration__floordiv(self, val):
637 """
638 Floor-divides this duration by a number or a duration.
639
640 @param val division factor, as `int` or `float` or `Duration` to divide by
641 @return new `Duration` instance if divided by a number,
642 or seconds as integer if divided by a `Duration`
643 """
644 if isinstance(val, numbers.Number):
645 return self.__class__(seconds=self.to_sec() // val)
646 if isinstance(val, rclpy.duration.Duration):
647 return int(self.to_sec() // val.to_sec())
648 return NotImplemented
649
650 def Duration__truediv(self, val):
651 """
652 Divides this duration by an integer or float.
653
654 @param val division factor, as `int` or `float` or `Duration` to divide by
655 @return new `Duration` instance if divided by a number,
656 or seconds as float if divided by a `Duration`
657 """
658 if isinstance(val, numbers.Number):
659 return self.__class__(seconds=self.to_sec() / val)
660 if isinstance(val, rclpy.duration.Duration):
661 return self.to_sec() / val.to_sec()
662 return NotImplemented
663
664 def Duration__mod(self, val):
665 """
666 Returns the remainder of dividing this duration by another duration.
667
668 @param val modulo factor, as `int` or `float` to mod by
669 @return new `Duration` instance of remaining time after mod
670 """
671 if isinstance(val, rclpy.duration.Duration):
672 return self.__class__(nanoseconds=self.nanoseconds % val.nanoseconds)
673 return NotImplemented
674
675 def Duration__divmod(self, val):
676 """
677 Returns the divmod result for this and another duration.
678
679 @param val division factor, as `Duration` to divide by
680 @return (division floored result as `int`, remaining time as `Duration`)
681 """
682 if isinstance(val, rclpy.duration.Duration):
683 quotient, remainder = divmod(self.nanoseconds, val.nanoseconds)
684 return int(quotient), self.__class__(nanoseconds=remainder)
685 return NotImplemented
686
687
688 ROS2_Service__init = rclpy.service.Service.__init__
689
690 def Service__init(self, service_handle, srv_type, srv_name, callback,
691 callback_group, qos_profile):
692 """Wraps Service.__init__() to support returning list/dict/None from server callback."""
693 callback = service_serve_wrapper(self, callback)
694 ROS2_Service__init(self, service_handle, srv_type, srv_name, callback,
695 callback_group, qos_profile)
696
697 def Service__spin(self):
698 """Spins ROS2 until service or node shutdown."""
699 try:
700 while not ros.SHUTDOWN and self.handle:
701 ros.spin_once(0.5)
702 except KeyboardInterrupt:
703 ros.get_logger().debug("keyboard interrupt, shutting down")
705
706 ROS2_Subscription__init = rclpy.subscription.Subscription.__init__
708 def Subscription__init(self, subscription_handle, msg_type, topic, callback,
709 callback_group, qos_profile, raw, event_callbacks):
710 """Wraps Subscription.__init__() to support AnyMsg and multiple callbacks."""
711 ROS2_Subscription__init(self, subscription_handle, msg_type, topic, self._invoke_callbacks,
712 callback_group, qos_profile, raw, event_callbacks)
713 self.impl = type("", (), {})() # Dummy object
714 self.impl.add_callback = functools.partial(Subscription__add_callback, self)
715 self.impl.remove_callback = functools.partial(Subscription__remove_callback, self)
716 self._callbacks = getattr(self, "_callbacks", [(callback, None)])
717 self._anymsg = False
718
720 """Returns callback args given in constructor."""
721 return self._callbacks[0][-1] if self._callbacks else None
722
723 def Subscription__invoke_callbacks(self, msg):
724 """Handler for received message, invokes all added callbacks."""
725 if self._anymsg and isinstance(msg, bytes):
726 msg = AnyMsg().deserialize(msg)
727 for cb, arg in list(self._callbacks):
728 try:
729 cb(msg) if arg is None else cb(msg, arg)
730 except Exception:
731 t = ("during shutdown, " if ros.SHUTDOWN else "") + "bad callback: %s\n%s"
732 ros.get_logger().warning(t, cb, traceback.format_exc())
733
734 def Subscription__add_callback(self, cb, cb_args):
735 """
736 Registers a callback to be invoked whenever a new message is received
737
738 @param cb callback function to invoke with message data instance, i.e. fn(data).
739 If callback args is set, they will be passed in as the second argument.
740 @param cb_cargs additional arguments to pass to callback
741 """
742 self._callbacks.append((cb, cb_args))
744 def Subscription__remove_callback(self, cb, cb_args):
745 """
746 Unregisters a message callback.
747
748 @param cb callback function
749 @param cb_cargs additional arguments associated with callback
750
751 @throws KeyError if no matching callback
752 """
753 matches = [x for x in self._callbacks if x[0] == cb and x[1] == cb_args]
754 if matches:
755 new_callbacks = self._callbacks[:]
756 # remove the first match
757 new_callbacks.remove(matches[0])
758 self._callbacks = new_callbacks
759 if not matches:
760 raise KeyError("no matching cb")
762
764 """Returns time remaining for rate to sleep, as `Duration`."""
765 return rclpy.duration.Duration(nanoseconds=self._timer.time_until_next_call())
766
767
768 ROS2_Timer__init = rclpy.timer.Timer.__init__
769
770 TIMER_COUNTER = 0
771
772 def Timer__init(self, callback, callback_group, timer_period_ns, clock, *, context=None):
773 """Constructs a new ROS2 `Timer`."""
774 global TIMER_COUNTER
775 ROS2_Timer__init(self, callback, callback_group, timer_period_ns, clock, context=context)
776 TIMER_COUNTER += 1
777 self._name = "%s-%s" % (self.handle.name, TIMER_COUNTER)
779 def Timer__ident(self):
780 """Returns timer identifier as a non-negative integer."""
781 return id(self)
783 def Timer__getName(self):
784 """Returns timer name."""
785 return self._name
787 def Timer__setName(self, name):
788 """Sets timer name."""
789 self._name = name
791 def Timer__isDaemon(self):
792 """Returns `True`."""
793 return True
794
795 def Timer__setDaemon(self, daemonic):
796 """Raises error."""
797 raise RuntimeError("cannot set daemon status of running thread")
798
799 def Timer__join(self):
800 """Returns when timer has terminated."""
801 while self.handle and self.is_ready():
802 time.sleep(1)
803
804 def Timer__is_alive(self):
805 """Returns whether timer is still running."""
806 return self.handle and self.is_ready()
807
809 ROS2_Time__init = rclpy.time.Time.__init__
810
811 def Time__init(self, secs=0, nsecs=0, *, seconds=0, nanoseconds=0,
812 clock_type=ClockType.SYSTEM_TIME):
813 """Constructs a new ROS2 `Time`, using either ROS1 or ROS2 arguments."""
814 if secs or nsecs: seconds, nanoseconds = secs, nsecs
815 ROS2_Time__init(self, seconds=seconds, nanoseconds=nanoseconds, clock_type=clock_type)
817 def TVal__is_nonzero(self):
818 """Returns whether value is not zero."""
819 return self.nanoseconds != 0
821 def TVal__is_zero(self):
822 """Returns whether value is zero."""
823 return self.nanoseconds == 0
825 def TVal__to_sec(self):
826 """Returns value in seconds as `float`."""
827 return ros.to_sec(self)
829 def TVal__to_nsec(self):
830 """Returns value in nanoseconds as `int`."""
831 return ros.to_nsec(self)
832
833 def TVal__secs(self):
834 """Returns the seconds portion as `int`."""
835 return self.nanoseconds // 10**9
836
837 def TVal__nsecs(self):
838 """Returns the nanoseconds portion as `int`."""
839 return self.nanoseconds % 10**9
840
841
842
843__all__ = [
844 "patch_ros1", "patch_ros1_rclify", "patch_ros2", "set_extra_attribute"
845]
Simple clock interface mimicking `rclpy.clock.Clock`, only providing `now()`.
Definition clock.py:35
ServiceProxy__service_is_ready(self)
Returns whether service is ready.
Definition patch.py:293
patch_ros1()
Patches ROS1 classes with generic compatibility with ROS2 and rosros.
Definition patch.py:56
TVal__is_nonzero(self)
Returns whether value is not zero.
Definition patch.py:808
Duration__mod(self, val)
Returns the remainder of dividing this duration by another duration.
Definition patch.py:662
Duration__floordiv(self, val)
Floor-divides this duration by a number or a duration.
Definition patch.py:637
Time__from_msg(cls, msg, clock_type=ClockType.ROS_TIME)
Returns a new Time from given.
Definition patch.py:377
Timer__timer_period_ns__setter(self, value)
Sets timer period in nanoseconds, resets timer if running.
Definition patch.py:468
Timer__timer_period_ns(self)
Returns timer period in nanoseconds.
Definition patch.py:464
Subscription__remove_callback(self, cb, cb_args)
Unregisters a message callback.
Definition patch.py:743
TVal__nsecs(self)
Returns the nanoseconds portion as `int`.
Definition patch.py:828
ROS1_ServiceProxy__call
Definition patch.py:254
ServiceProxy__call_async(self, *args, **kwargs)
Make a service request and asyncronously get the result.
Definition patch.py:273
service_serve_wrapper(self, serve)
Returns service serve-function wrapped to ensure return with response instance.
Definition patch.py:225
TVal__secs(self)
Returns the seconds portion as `int`.
Definition patch.py:824
Time__init(self, secs=0, nsecs=0, *seconds=0, nanoseconds=0, clock_type=ClockType.SYSTEM_TIME)
Constructs a new ROS1 `Time`, using either ROS1 or ROS2 arguments.
Definition patch.py:356
Timer__join(self)
Returns when timer has terminated.
Definition patch.py:790
Duration__sub(self, other)
Subtracts a duration from this duration.
Definition patch.py:616
Timer__run(self)
Override rospy.Timer.run() to support canceling and resetting, and call timestamps.
Definition patch.py:408
ServiceProxy__call(self, *args, **kwargs)
Calls the service, supports giving request field values as dictionary in args[0].
Definition patch.py:256
ROS1_Service__init
Definition patch.py:246
TVal__to_nsec(self)
Returns value in nanoseconds as `int`.
Definition patch.py:820
Timer__setName(self, name)
Sets timer name.
Definition patch.py:778
Timer__cancel(self)
Sets timer on pause, until reset or shutdown.
Definition patch.py:448
ROS1_SubscriberImpl__receive_callback
Definition patch.py:318
TVal__is_zero(self)
Returns whether value is zero.
Definition patch.py:812
Service__spin(self)
Spins ROS2 until service or node shutdown.
Definition patch.py:688
Duration__truediv(self, val)
Divides this duration by an integer or float.
Definition patch.py:650
Duration__mul(self, val)
Multiplies this duration by an integer or float.
Definition patch.py:626
Rate__remaining(self)
Returns time remaining for rate to sleep, as `Duration`.
Definition patch.py:754
Subscription__add_callback(self, cb, cb_args)
Registers a callback to be invoked whenever a new message is received.
Definition patch.py:732
Duration__add(self, other)
Returns a new duration, adding given duration to this.
Definition patch.py:606
Time__del(self)
Clear object-related data, if any.
Definition patch.py:363
Subscription__callback_args(self)
Returns callback args given in constructor.
Definition patch.py:710
Publisher__assert_liveliness(self)
Does nothing (ROS2 compatibility stand-in).
Definition patch.py:233
Timer__shutdown(self)
Stop firing callbacks.
Definition patch.py:403
Timer__isDaemon(self)
Returns `True`.
Definition patch.py:782
ServiceProxy__remove_pending_request(self, future)
Does nothing (ROS2 compatibility stand-in).
Definition patch.py:284
Duration__divmod(self, val)
Returns the divmod result for this and another duration.
Definition patch.py:672
publish_wrapper(publish)
Returns publish-function wrapped to ensure invoke with message instance.
Definition patch.py:574
Duration__init(self, secs=0, nsecs=0, *seconds=0, nanoseconds=0)
Constructs a new `Duration`, using either ROS1 or ROS2 arguments.
Definition patch.py:337
Timer__init(self, period, callback, oneshot=False, reset=False)
Constructs a new ROS1 Timer.
Definition patch.py:395
set_extra_attribute(obj, name, value)
Sets value for object patched attribute.
Definition patch.py:215
Subscription__init(self, subscription_handle, msg_type, topic, callback, callback_group, qos_profile, raw, event_callbacks)
Wraps Subscription.__init__() to support AnyMsg and multiple callbacks.
Definition patch.py:701
Duration__abs(self)
Returns absolute value of this duration.
Definition patch.py:595
Duration__neg(self)
Returns negative value of this duration.
Definition patch.py:591
patch_ros1_rclify()
Patches ROS1 classes with full stand-ins for ROS2 methods and properties.
Definition patch.py:76
Time__clock_type(self)
Returns the ClockType of this Time.
Definition patch.py:371
ROS2_Service__init
Definition patch.py:679
Temporal__to_msg(self)
Returns a new Time or Duration from this.
Definition patch.py:342
Timer__ident(self)
Returns timer identifier as a non-negative integer.
Definition patch.py:770
Time__seconds_nanoseconds(self)
Returns time as (seconds, nanoseconds).
Definition patch.py:367
Timer__is_alive(self)
Returns whether timer is still running.
Definition patch.py:795
Timer__time_since_last_call(self)
Returns nanoseconds since last timer callback (ROS2 compatibility stand-in).
Definition patch.py:483
Timer__is_ready(self)
Returns whether timer callback should be called right now.
Definition patch.py:477
ROS1_Subscriber__init
Definition patch.py:317
Duration__from_msg(cls, msg)
Returns a new Duration from given.
Definition patch.py:347
Subscription__invoke_callbacks(self, msg)
Handler for received message, invokes all added callbacks.
Definition patch.py:714
rostimer_sleep_on_event(duration, event=None)
Sleeps for the specified duration in ROS time, or until event gets set.
Definition patch.py:506
ROS1_Publisher__publish
Definition patch.py:237
Subscriber__init(self, name, data_class, callback=None, callback_args=None, queue_size=None, buff_size=65536, tcp_nodelay=False)
Wraps rospy.Subscriber.__init__() to add raw bytes autoconversion support.
Definition patch.py:322
Timer__is_canceled(self)
Returns whether the timer has been canceled (paused until reset or shutdown).
Definition patch.py:473
Timer__time_until_next_call(self)
Returns nanoseconds until next timer callback (ROS2 compatibility stand-in).
Definition patch.py:487
Timer__reset(self)
Sets last call time to now, postponing next callback by full period; uncancels if canceled.
Definition patch.py:455
ROS2_Subscription__init
Definition patch.py:697
patch_ros2()
Patches ROS2 classes with full stand-ins for ROS1 methods and properties.
Definition patch.py:127
Timer__setDaemon(self, daemonic)
Raises error.
Definition patch.py:786
ROS2_Duration__init
Definition patch.py:584
Timer__getName(self)
Returns timer name.
Definition patch.py:774
SubscriberImpl__receive_callback(self, msgs, connection)
Wraps rospy.topics.SubscriberImpl.receive_callback() with raw bytes autoconversion.
Definition patch.py:328
TVal__to_sec(self)
Returns value in seconds as `float`.
Definition patch.py:816
Service__init(self, name, service_class, handler, buff_size=65536, error_handler=None)
Wraps Service.__init__() to support returning None from server callback.
Definition patch.py:248
ROS1_Duration__init
Definition patch.py:335
ServiceProxy__wait_for_service(self, timeout=None, timeout_sec=None)
Waits for a service server to become ready.
Definition patch.py:308
Timer__clock(self)
Returns the Clock used by this timer.
Definition patch.py:460
client_call_wrapper(call)
Returns service client call-function wrapped to ensure invoke with request instance.
Definition patch.py:550
Publisher__publish(self, *args, **kwds)
Publishes message, supports giving message field values as dictionary in args[0].
Definition patch.py:239