# Software License Agreement (BSD License)
#
# Copyright (c) 2008, Willow Garage, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Willow Garage, Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""ROS Time representation, including Duration."""
from __future__ import division
import numbers
def _canon(secs, nsecs):
# canonical form: nsecs is always positive, nsecs < 1 second
secs_over = nsecs // 1000000000
secs += secs_over
nsecs -= secs_over * 1000000000
return secs, nsecs
[docs]class TVal(object):
"""
Base class of :class:`Time` and :class:`Duration` representations.
Representation is secs+nanoseconds since epoch.
"""
__slots__ = ['secs', 'nsecs']
# mimic same API as messages when being introspected
_slot_types = ['int32', 'int32']
def __init__(self, secs=0, nsecs=0): # noqa: D205, D400
"""
:param secs: seconds. If secs is a float, then nsecs must not be set or 0,
larger seconds will be of type long on 32-bit systems, ``int/long/float``
:param nsecs: nanoseconds, ``int``
"""
if not isinstance(secs, numbers.Integral):
# float secs constructor
if nsecs != 0:
raise ValueError('if secs is a float, nsecs cannot be set')
float_secs = secs
secs = int(float_secs)
nsecs = int((float_secs - secs) * 1000000000)
else:
secs = int(secs)
nsecs = int(nsecs)
self.secs, self.nsecs = _canon(secs, nsecs)
[docs] @classmethod
def from_sec(cls, float_secs):
"""
Create new TVal instance using time.time() value (float seconds).
:param float_secs: time value in time.time() format, ``float``
:returns: :class:`TVal` instance for specified time
"""
secs = int(float_secs)
nsecs = int((float_secs - secs) * 1000000000)
return cls(secs, nsecs)
[docs] def is_zero(self): # noqa: D200, D400, D401
"""
:returns: ``True`` if time is zero (secs and nsecs are zero), ``bool``
"""
return self.secs == 0 and self.nsecs == 0
[docs] def set(self, secs, nsecs):
"""
Set time using separate secs and nsecs values.
:param secs: seconds since epoch, ``int``
:param nsecs: nanoseconds since seconds, ``int``
"""
self.secs = secs
self.nsecs = nsecs
[docs] def canon(self):
"""
Canonicalize the field representation in this instance.
Should only be used when manually setting secs/nsecs slot values, as
in deserialization.
"""
self.secs, self.nsecs = _canon(self.secs, self.nsecs)
[docs] def to_sec(self): # noqa: D200, D400, D401
"""
:returns: time as float seconds (same as time.time() representation), ``float``
"""
return float(self.secs) + float(self.nsecs) / 1e9
[docs] def to_nsec(self): # noqa: D200, D400, D401
"""
:returns: time as nanoseconds, ``long``
"""
return self.secs * int(1e9) + self.nsecs
def __hash__(self):
"""
Time values are hashable.
Time values with identical fields have the same hash.
"""
return hash((self.secs, self.nsecs))
def __str__(self):
return str(self.to_nsec())
def __repr__(self):
return 'genpy.TVal[%d]' % self.to_nsec()
def __nonzero__(self):
"""Return if time value is not zero."""
return self.secs != 0 or self.nsecs != 0
__bool__ = __nonzero__
def __lt__(self, other):
"""< test for time values."""
try:
return self.__cmp__(other) < 0
except TypeError:
return NotImplemented
def __le__(self, other):
"""<= test for time values."""
try:
return self.__cmp__(other) <= 0
except TypeError:
return NotImplemented
def __gt__(self, other):
"""> test for time values."""
try:
return self.__cmp__(other) > 0
except TypeError:
return NotImplemented
def __ge__(self, other):
""">= test for time values."""
try:
return self.__cmp__(other) >= 0
except TypeError:
return NotImplemented
def __ne__(self, other):
return not self.__eq__(other)
def __cmp__(self, other):
if not isinstance(other, TVal):
raise TypeError('Cannot compare to non-TVal')
a = self.to_nsec()
b = other.to_nsec()
return (a > b) - (a < b)
def __eq__(self, other):
if not isinstance(other, TVal):
return False
return self.to_nsec() == other.to_nsec()
[docs]class Time(TVal):
"""
Time contains the ROS-wide 'time' primitive representation.
It consists of two integers: seconds since epoch and nanoseconds since
seconds. Time instances are mutable.
"""
__slots__ = ['secs', 'nsecs']
def __init__(self, secs=0, nsecs=0):
"""
Construct time where secs and nsecs are integers.
You may prefer to use the static L{from_sec()} factory method instead.
:param secs: seconds since epoch, ``int``
:param nsecs: nanoseconds since seconds (since epoch), ``int``
"""
super(Time, self).__init__(secs, nsecs)
if self.secs < 0:
raise TypeError('time values must be positive')
def __getstate__(self):
"""Support for Python pickling."""
return [self.secs, self.nsecs]
def __setstate__(self, state):
"""Support for Python pickling."""
self.secs, self.nsecs = state
[docs] def to_time(self):
"""
Get Time in time.time() format. alias of L{to_sec()}.
:returns: time in floating point secs (time.time() format), ``float``
"""
return self.to_sec()
def __hash__(self):
return super(Time, self).__hash__()
def __repr__(self):
return 'genpy.Time[%d]' % self.to_nsec()
def __add__(self, other):
"""
Add duration to this time.
:param other: :class:`Duration`
"""
if not isinstance(other, Duration):
return NotImplemented
return self.__class__(self.secs + other.secs, self.nsecs + other.nsecs)
__radd__ = __add__
def __sub__(self, other):
"""
Subtract time or duration from this time.
:param other: :class:`Duration`/:class:`Time`
:returns: :class:`Duration` if other is a :class:`Time`, :class:`Time` if other is a :class:`Duration`
"""
if isinstance(other, Time):
return Duration(self.secs - other.secs, self.nsecs - other.nsecs)
elif isinstance(other, Duration):
return self.__class__(self.secs - other.secs, self.nsecs - other.nsecs)
else:
return NotImplemented
def __cmp__(self, other):
"""
Compare to another time.
:param other: :class:`Time`
"""
if not isinstance(other, Time):
raise TypeError('cannot compare to non-Time')
a = self.to_nsec()
b = other.to_nsec()
return (a > b) - (a < b)
def __eq__(self, other):
"""
Equal test for Time.
Comparison assumes that both time instances are in canonical
representation; only compares fields.
:param other: :class:`Time`
"""
if not isinstance(other, Time):
return False
return self.secs == other.secs and self.nsecs == other.nsecs
[docs]class Duration(TVal):
"""
Duration represents the ROS 'duration' primitive.
It consists of two integers: seconds and nanoseconds.
The Duration class allows you to add and subtract Duration instances,
including adding and subtracting from :class:`Time` instances.
"""
__slots__ = ['secs', 'nsecs']
def __init__(self, secs=0, nsecs=0):
"""
Create new Duration instance. secs and nsecs are integers and correspond to the ROS 'duration' primitive.
:param secs: seconds, ``int``
:param nsecs: nanoseconds, ``int``
"""
super(Duration, self).__init__(secs, nsecs)
def __getstate__(self):
"""Support for Python pickling."""
return [self.secs, self.nsecs]
def __setstate__(self, state):
"""Support for Python pickling."""
self.secs, self.nsecs = state
def __hash__(self):
return super(Duration, self).__hash__()
def __repr__(self):
return 'genpy.Duration[%d]' % self.to_nsec()
def __neg__(self): # noqa: D400, D401
""":returns: Negative value of this :class:`Duration`"""
return self.__class__(-self.secs, -self.nsecs)
def __abs__(self):
"""
Absolute value of this duration.
:returns: positive :class:`Duration`
"""
if self.secs >= 0:
return self
return self.__neg__()
def __add__(self, other):
"""
Add duration to this duration, or this duration to a time, creating a new time value as a result.
:param other: duration or time, ``Duration``/``Time``
:returns: :class:`Duration` if other is a :class:`Duration`
instance, :class:`Time` if other is a :class:`Time`
"""
if isinstance(other, Duration):
return self.__class__(self.secs+other.secs, self.nsecs+other.nsecs)
else:
return NotImplemented
__radd__ = __add__
def __sub__(self, other):
"""
Subtract duration from this duration, returning a new duration.
:param other: duration
:returns: :class:`Duration`
"""
if not isinstance(other, Duration):
return NotImplemented
return self.__class__(self.secs-other.secs, self.nsecs-other.nsecs)
def __mul__(self, val):
"""
Multiply this duration by an integer or float.
:param val: multiplication factor, ``int/float``
:returns: :class:`Duration` multiplied by val
"""
if isinstance(val, numbers.Integral):
return self.__class__(self.secs * val, self.nsecs * val)
elif isinstance(val, numbers.Real):
return self.__class__.from_sec(self.to_sec() * val)
else:
return NotImplemented
__rmul__ = __mul__
def __floordiv__(self, val):
"""
Floor divide this duration by an integer or float.
:param val: division factor ``int/float``, or :class:`Duration` to divide by
:returns: :class:`Duration` divided by val - a :class:`Duration` if divided by a number, or a number if divided by a duration
"""
if isinstance(val, numbers.Integral) or isinstance(val, numbers.Real):
return self.__class__.from_sec(self.to_sec() // val)
elif isinstance(val, Duration):
return int(self.to_sec() // val.to_sec())
else:
return NotImplemented
def __div__(self, val):
"""
Divide this duration by an integer or float.
:param val: division factor ``int/float``, or :class:`Duration` to divide by
:returns: :class:`Duration` divided by val - a :class:`Duration` if divided by a number, or a number if divided by a duration
"""
if isinstance(val, numbers.Integral) or isinstance(val, numbers.Real):
return self.__class__.from_sec(self.to_sec() / val)
elif isinstance(val, Duration):
return self.to_sec() / val.to_sec()
else:
return NotImplemented
def __truediv__(self, val):
"""
Divide this duration by an integer or float.
:param val: division factor ``int/float``, or :class:`Duration` to divide by
:returns: :class:`Duration` divided by val - a :class:`Duration` if divided by a number, or a number if divided by a duration
"""
if isinstance(val, numbers.Real):
return self.__class__.from_sec(self.to_sec() / val)
elif isinstance(val, Duration):
return self.to_sec() / val.to_sec()
else:
return NotImplemented
def __mod__(self, val):
"""
Find the remainder when dividing this Duration by another Duration.
:returns: :class:`Duration` The remaining time after the division
"""
if isinstance(val, Duration):
return self.__class__.from_sec(self.to_sec() % val.to_sec())
else:
return NotImplemented
def __divmod__(self, val):
"""
Implement the builtin divmod for a pair of Durations.
:returns: ``int`` The floored result of the division
:returns: :class:`Duration` The remaining time after the division
"""
if isinstance(val, Duration):
quotient, remainder = divmod(self.to_sec(), val.to_sec())
return int(quotient), self.__class__.from_sec(remainder)
else:
return NotImplemented
def __cmp__(self, other):
if not isinstance(other, Duration):
raise TypeError('Cannot compare to non-Duration')
a = self.to_nsec()
b = other.to_nsec()
return (a > b) - (a < b)
def __eq__(self, other):
if not isinstance(other, Duration):
return False
return self.secs == other.secs and self.nsecs == other.nsecs