tester.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 # Copyright (c) 2009, Willow Garage, Inc.
3 # All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 # * Neither the name of the Willow Garage, Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 
29 # Author: Stuart Glaser
30 
31 # This is largely copied from bondpy, with hooks that "break" it for testing purposes.
32 from __future__ import print_function
33 
34 import threading
35 import time
36 import uuid
37 
38 import roslib; roslib.load_manifest('test_bond')
39 import rospy
40 from bond.msg import Constants, Status
41 from test_bond.srv import TestBond, TestBondResponse
42 
43 import atexit
44 atexit.register(rospy.signal_shutdown, 'exit')
45 
46 import BondSM_sm
47 
48 
49 class Timeout:
50  def __init__(self, duration, on_timeout=None):
51  self.duration = duration
52  self.timer = threading.Timer(0, self._on_timer)
53  self.deadline = rospy.Time.now()
54  self.on_timeout = on_timeout
55 
56  def reset(self):
57  self.timer.cancel()
58  self.timer = threading.Timer(self.duration.to_sec(), self._on_timer)
59  self.timer.start()
60  self.deadline = rospy.Time.now() + self.duration
61  return self
62 
63  def cancel(self):
64  self.timer.cancel()
65 
66  def left(self):
67  return max(rospy.Duration(0), self.deadline - rospy.Time.now())
68 
69  def _on_timer(self):
70  if self.on_timeout:
71  self.on_timeout()
72 
73 
74 class BondTester:
75  def __init__(self, req):
76  self.req = req
77  self.topic = req.topic
78  self.id = req.id
79  self.instance_id = str(uuid.uuid4())
80  self.on_death = None
81  self.on_life = None
82  self.is_shutdown = False
83  self.sister_died_first = False
84  self.death_started = None
85 
86  self.sm = BondSM_sm.BondSM_sm(self)
87  # self.sm.setDebugFlag(True)
88  self.lock = threading.RLock()
89  self.condition = threading.Condition(self.lock)
90 
92  rospy.Duration(Constants.DEFAULT_CONNECT_TIMEOUT), self._on_connect_timeout)
94  rospy.Duration(Constants.DEFAULT_HEARTBEAT_TIMEOUT), self._on_heartbeat_timeout)
96  rospy.Duration(Constants.DEFAULT_DISCONNECT_TIMEOUT), self._on_disconnect_timeout)
97 
98  self.connect_timeout.reset()
99 
100  self.sub = rospy.Subscriber(self.topic, Status, self._on_bond_status)
101  self.pub = rospy.Publisher(self.topic, Status)
102 
103  self.thread = threading.Thread(target=self._publishing_thread)
104  self.thread.daemon = True
105  self.thread.start()
106 
107  if req.delay_death >= rospy.Duration(0.0):
108  self.death_timeout = Timeout(req.delay_death, self.die).reset()
109 
110  self.deadline = None
111 
113  with self.lock:
114  self.sm.ConnectTimeout()
115 
117  if self.req.inhibit_death:
118  return
119  with self.lock:
120  self.sm.HeartbeatTimeout()
121 
123  with self.lock:
124  self.sm.DisconnectTimeout()
125 
126  def __del__(self):
127  self.shutdown()
128 
129  def shutdown(self):
130  if not self.is_shutdown:
131  with self.lock:
132  self.is_shutdown = True
133  if self.sm.getState().getName() != 'SM.Dead':
134  print("I'm not dead yet:%s in %s" % (self.id, self.sm.getState().getName()))
135  self.die()
136  self.sub.unregister()
137  self.pub.unregister()
138  self.condition.notify_all()
139  print("Unregistered")
140 
141  def _on_bond_status(self, msg):
142  # Filters out messages from other bonds and messages from ourselves
143  if msg.id == self.id and msg.instance_id != self.instance_id:
144  with self.lock:
145  if msg.active:
146  if not self.is_dead():
147  self.sm.SisterAlive()
148  else:
149  if self.req.inhibit_death:
150  return
151 
152  self.sm.SisterDead()
153 
154  # Immediate ack for sister's death notification
155  if self.sister_died_first:
156  self._publish(False)
157 
158  def _publish(self, active):
159  msg = Status()
160  msg.header.stamp = rospy.Time.now()
161  msg.id = self.id
162  msg.instance_id = self.instance_id
163  msg.active = active
164 
165  if not msg.active and self.req.inhibit_death_message:
166  pass
167  else:
168  self.pub.publish(msg)
169 
171 
172  time.sleep(self.req.delay_connect.to_sec())
173 
174  with self.lock:
175  # Publishing ALIVE
176  while not self.is_shutdown and self.sm.getState().getName() in [
177  'SM.WaitingForSister', 'SM.Alive']:
178  self._publish(True)
179  self.condition.wait(Constants.DEFAULT_HEARTBEAT_PERIOD)
180 
181  # Publishing DEAD
182  while not self.is_shutdown and self.sm.getState().getName() == 'SM.AwaitSisterDeath':
183  self._publish(False)
184  self.condition.wait(Constants.DEAD_PUBLISH_PERIOD)
185 
186  def Connected(self):
187  self.connect_timeout.cancel()
188  self.condition.notify_all()
189  if self.on_life:
190  self.on_life()
191 
192  def Heartbeat(self):
193  self.heartbeat_timeout.reset()
194 
195  def SisterDied(self):
196  self.sister_died_first = True
197 
198  def Death(self):
199  self.condition.notify_all()
200  self.heartbeat_timeout.cancel()
201  self.disconnect_timeout.cancel()
202  if not self.death_started:
203  self.death_started = rospy.Time.now()
204 
205  def StartDying(self):
206  self.heartbeat_timeout.cancel()
207  self.disconnect_timeout.reset()
208  if not self.death_started:
209  self.death_started = rospy.Time.now()
210 
211  def wait_for_life(self, timeout=None):
212  if self.deadline:
213  self.deadline.cancel()
214  self.deadline = None
215  if timeout:
216  self.deadline = Timeout(timeout).reset()
217  with self.lock:
218  while self.sm.getState().getName() == 'SM.WaitingForSister':
219  if self.deadline and self.deadline.left() == rospy.Duration(0):
220  break
221  self.condition.wait(self.deadline and self.deadline.left().to_sec())
222  return self.sm.getState().getName() != 'SM.WaitingForSister'
223 
224  def wait_for_death(self, timeout=None):
225  if self.deadline:
226  self.deadline.cancel()
227  self.deadline = None
228  if timeout:
229  self.deadline = Timeout(timeout).reset()
230  with self.lock:
231  while self.sm.getState().getName() != 'SM.Dead':
232  if self.deadline and self.deadline.left() == rospy.Duration(0):
233  break
234  self.condition.wait(self.deadline and self.deadline.left().to_sec())
235  return self.sm.getState().getName() == 'SM.Dead'
236 
237  def is_dead(self):
238  with self.lock:
239  return self.sm.getState().getName() == 'SM.Dead'
240 
241  def die(self):
242  with self.lock:
243  self.sm.Die()
244  self._publish(False)
245 
246  def __repr__(self):
247  return "[Bond %s, Instance %s (%s)]" % (
248  self.id, self.instance_id, self.sm.getState().getName())
249 
250 
251 class Tester:
252  def __init__(self):
253  self.bond_tester = None
254  self.service = rospy.Service('test_bond', TestBond, self._test_bond)
255 
256  def _test_bond(self, req):
257  print("TEST")
258  if self.bond_tester:
259  self.bond_tester.shutdown()
260  self.bond_tester = BondTester(req)
261  print("Test bond instance id: %s" % self.bond_tester.instance_id)
262  return TestBondResponse()
263 
264 
265 def main():
266  rospy.init_node('bond_tester', anonymous=True, disable_signals=True)
267  tester = Tester() # noqa
268  rospy.spin()
269 
270 
271 if __name__ == '__main__':
272  main()
def __del__(self)
Definition: tester.py:126
def _on_bond_status(self, msg)
Definition: tester.py:141
def is_dead(self)
Definition: tester.py:237
def _publish(self, active)
Definition: tester.py:158
def Heartbeat(self)
Definition: tester.py:192
def reset(self)
Definition: tester.py:56
def StartDying(self)
Definition: tester.py:205
def __init__(self, req)
Definition: tester.py:75
def die(self)
Definition: tester.py:241
def SisterDied(self)
Definition: tester.py:195
def main()
Definition: tester.py:265
def Connected(self)
Definition: tester.py:186
def __init__(self)
Definition: tester.py:252
def wait_for_life(self, timeout=None)
Definition: tester.py:211
def _on_timer(self)
Definition: tester.py:69
def left(self)
Definition: tester.py:66
def __init__(self, duration, on_timeout=None)
Definition: tester.py:50
def shutdown(self)
Definition: tester.py:129
def _on_connect_timeout(self)
Definition: tester.py:112
def __repr__(self)
Definition: tester.py:246
def Death(self)
Definition: tester.py:198
def _test_bond(self, req)
Definition: tester.py:256
def wait_for_death(self, timeout=None)
Definition: tester.py:224
def _on_heartbeat_timeout(self)
Definition: tester.py:116
def cancel(self)
Definition: tester.py:63
def _on_disconnect_timeout(self)
Definition: tester.py:122
def _publishing_thread(self)
Definition: tester.py:170


test_bond
Author(s): Stuart Glaser
autogenerated on Wed Sep 2 2020 03:40:47