timeline.py
Go to the documentation of this file.
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2014, Austin Hendrix
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Austin Hendrix. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 # Author: Austin Hendrix
34 
35 from collections import deque
36 from python_qt_binding.QtCore import Signal, Slot, QObject
37 
38 import rospy
39 import threading
40 
41 from diagnostic_msgs.msg import DiagnosticArray
42 
43 
44 class Timeline(QObject):
45  """
46  A class which represents the status history of diagnostics
47  It can be queried for a past history of diagnostics, and paused
48  """
49  message_updated = Signal(dict)
50  queue_updated = Signal()
51  pause_changed = Signal(bool)
52  position_changed = Signal(int)
53 
54  def __init__(self, topic, topic_type, count=30):
55  super(Timeline, self).__init__()
56  self._mutex = threading.RLock()
57  self._queue = deque(maxlen=count)
58  self._count = count
59  self._current_index = -1 # rightmost item
60 
61  # the paused queue is a backup copy of the queue that is updated with
62  # new messages while the timeline is paused, so that new messages and
63  # new history are not lost
64  self._paused_queue = None
65 
67 
68  self._subscriber = rospy.Subscriber(topic, topic_type, self.callback,
69  queue_size=10)
70 
71  def shutdown(self):
72  """
73  Turn off this Timeline
74  Internally, this just shuts down the subscriber
75  """
76  self._subscriber.unregister()
77 
78  @Slot(bool)
79  def set_paused(self, pause):
80  """
81  Slot, to be called to change the pause status of the timeline
82 
83  This is generally intended to be connected to the status signal
84  from a button or checkbox
85  """
86  if pause != self.paused:
87  with self._mutex:
88  if pause:
89  self._paused_queue = deque(self._queue, self._queue.maxlen)
90  else:
91  self._queue = self._paused_queue
92  self._paused_queue = None
93 
94  # update pointer to latest message
95  self._current_index = -1
96  self.message_updated.emit(self._queue[self.position])
97  self.pause_changed.emit(pause)
98 
99  @property
100  def paused(self):
101  """ True if this timeline is paused """
102  with self._mutex:
103  return self._paused_queue is not None
104 
105  def callback(self, msg):
106  """
107  ROS Callback for new diagnostic messages
108 
109  Puts new msg into the queue, and emits a signal to let listeners know
110  that the timeline has been updated
111 
112  If the timeline is paused, new messages are placed into a separate
113  queue and swapped back in when the timeline is unpaused
114 
115  :type msg: Either DiagnosticArray or DiagnosticsStatus. Can be
116  determined by __init__'s arg "msg_callback".
117  """
118  self._last_message_time = rospy.get_time()
119  dic = {status.name: status for status in msg.status}
120 
121  with self._mutex:
122  if self.paused:
123  self._paused_queue.append(dic)
124  else:
125  self._queue.append(dic)
126  self.queue_updated.emit()
127  if self.position == -1:
128  self.message_updated.emit(dic)
129 
130  @property
131  def has_messages(self):
132  """
133  True if this timeline has received any messages.
134  False if no messages have been received yet
135  """
136  with self._mutex:
137  return len(self._queue) > 0
138 
139  @property
140  def data_age(self):
141  """ Get the age (in seconds) of the most recent diagnostic message """
142  current_time = rospy.get_time()
143  time_diff = current_time - self._last_message_time
144  return time_diff
145 
146  @property
147  def is_stale(self):
148  """ True is this timeline is stale. """
149  return self.data_age > 10.0
150 
151  @property
152  def position(self):
153  index = self._current_index
154  with self._mutex:
155  while index < -1:
156  index = len(self._queue) + index
157  if index == len(self._queue) - 1:
158  index = -1
159  return index
160 
161  @position.setter
162  def position(self, index):
163  with self._mutex:
164  max_index = len(self._queue) - 1
165  min_index = -len(self._queue)
166  index = min(index, max_index)
167  index = max(index, min_index)
168  if index != self._current_index or self._current_index == -1:
169  self._current_index = index
170  self.message_updated.emit(self._queue[index])
171 
172  @Slot(int)
173  def set_position(self, position):
174  self.position = position
175  self.position_changed.emit(position)
176 
177  def get_current_status_by_name(self, name):
178  with self._mutex:
179  return self._queue[self.position][name]
180 
181  def get_all_status_by_name(self, name):
182  with self._mutex:
183  try:
184  return [status[name] for status in list(self._queue)]
185  except:
186  return None
187 
188  def __len__(self):
189  with self._mutex:
190  return len(self._queue)
191 
192  def __iter__(self):
193  with self._mutex:
194  for msg in list(self._queue):
195  yield msg
def set_position(self, position)
Definition: timeline.py:173
def get_current_status_by_name(self, name)
Definition: timeline.py:177
def get_all_status_by_name(self, name)
Definition: timeline.py:181
def __init__(self, topic, topic_type, count=30)
Definition: timeline.py:54


rqt_robot_monitor
Author(s): Austin Hendrix, Isaac Saito, Ze'ev Klapow, Kevin Watts, Josh Faust
autogenerated on Thu Jun 4 2020 03:46:53