timeline.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2014, Austin Hendrix
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Austin Hendrix. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 #
00033 # Author: Austin Hendrix
00034 
00035 import copy
00036 from collections import deque
00037 from python_qt_binding.QtCore import Signal, Slot, QObject
00038 
00039 import rospy
00040 
00041 from diagnostic_msgs.msg import DiagnosticArray
00042 
00043 class Timeline(QObject):
00044     """
00045     A class which represents the status history of diagnostics
00046     It can be queried for a past history of diagnostics, and paused
00047     """
00048     message_updated = Signal(DiagnosticArray)
00049     queue_updated = Signal(deque)
00050     pause_changed = Signal(bool)
00051 
00052     def __init__(self, topic, topic_type, count=30):
00053         super(Timeline, self).__init__()
00054         self._queue = deque(maxlen=count)
00055         self._queue_copy = deque(maxlen=count)
00056         self._count = count
00057         self._current_index = -1 # rightmost item
00058 
00059         # the paused queue is a backup copy of the queue that is updated with
00060         # new messages while the timeline is paused, so that new messages and
00061         # new history are not lost
00062         self._paused_queue = None
00063 
00064         self._have_messages = False
00065         self._last_message_time = 0
00066 
00067         self._subscriber = rospy.Subscriber(topic, topic_type, self.callback,
00068                                             queue_size=10)
00069 
00070     def shutdown(self):
00071         """
00072         Turn off this Timeline
00073         Internally, this just shuts down the subscriber
00074         """
00075         self._subscriber.unregister()
00076 
00077     @Slot(bool)
00078     def set_paused(self, pause):
00079         """
00080         Slot, to be called to change the pause status of the timeline
00081 
00082         This is generally intended to be connected to the status signal
00083         from a button or checkbox
00084         """
00085         if pause != self.paused:
00086             if pause:
00087                 self._paused_queue = deque(self._queue, self._queue.maxlen)
00088             else:
00089                 self._queue = self._paused_queue
00090                 self._queue_copy = copy.deepcopy(self._paused_queue)
00091                 self._paused_queue = None
00092 
00093                 # update pointer to latest message
00094                 self._current_index = -1
00095                 self.message_updated.emit(self._queue[self._current_index])
00096             self.pause_changed.emit(pause)
00097 
00098     @property
00099     def paused(self):
00100         """ True if this timeline is paused """
00101         return self._paused_queue is not None
00102 
00103     def callback(self, msg):
00104         """
00105         ROS Callback for new diagnostic messages
00106 
00107         Puts new msg into the queue, and emits a signal to let listeners know
00108         that the timeline has been updated
00109 
00110         If the timeline is paused, new messages are placed into a separate
00111         queue and swapped back in when the timeline is unpaused
00112 
00113         :type msg: Either DiagnosticArray or DiagnosticsStatus. Can be
00114                    determined by __init__'s arg "msg_callback".
00115         """
00116         self._have_messages = True
00117         self._last_message_time = rospy.get_time()
00118         if self.paused:
00119             self._paused_queue.append(msg)
00120         else:
00121             self._queue.append(msg)
00122             self._queue_copy = copy.deepcopy(self._queue)
00123             self.queue_updated.emit(self._queue_copy)
00124             self.message_updated.emit(msg)
00125 
00126     @property
00127     def queue(self):
00128         return self._queue_copy
00129 
00130     @property
00131     def has_messages(self):
00132         """
00133         True if this timeline has received any messages.
00134         False if no messages have been received yet
00135         """
00136         return self._have_messages
00137 
00138     def data_age(self):
00139         """ Get the age (in seconds) of the most recent diagnostic message """
00140         current_time = rospy.get_time()
00141         time_diff = current_time - self._last_message_time
00142         return time_diff
00143 
00144     @property
00145     def is_stale(self):
00146         """ True is this timeline is stale. """
00147         return self.data_age() > 10.0
00148 
00149     def set_position(self, index):
00150         max_index = len(self._queue) - 1
00151         min_index = -len(self._queue)
00152         index = min(index, max_index)
00153         index = max(index, min_index)
00154         if index != self._current_index:
00155             self._current_index = index
00156             self.message_updated.emit(self._queue[index])
00157 
00158     def get_position(self):
00159         index = self._current_index
00160         if index < 0:
00161             index = len(self._queue) + index
00162         return index
00163 
00164     def __len__(self):
00165         return len(self._queue)
00166 
00167     def __iter__(self):
00168         for msg in self._queue:
00169             yield msg


rqt_robot_monitor
Author(s): Austin Hendrix, Isaac Saito, Ze'ev Klapow, Kevin Watts, Josh Faust
autogenerated on Tue Sep 26 2017 02:44:21