tester.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 # Copyright (c) 2009, Willow Garage, Inc.
00003 # All rights reserved.
00004 #
00005 # Redistribution and use in source and binary forms, with or without
00006 # modification, are permitted provided that the following conditions are met:
00007 #
00008 #     * Redistributions of source code must retain the above copyright
00009 #       notice, this list of conditions and the following disclaimer.
00010 #     * Redistributions in binary form must reproduce the above copyright
00011 #       notice, this list of conditions and the following disclaimer in the
00012 #       documentation and/or other materials provided with the distribution.
00013 #     * Neither the name of the Willow Garage, Inc. nor the names of its
00014 #       contributors may be used to endorse or promote products derived from
00015 #       this software without specific prior written permission.
00016 #
00017 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027 # POSSIBILITY OF SUCH DAMAGE.
00028 
00029 # Author: Stuart Glaser
00030 
00031 # This is largely copied from bondpy, with hooks that "break" it for testing purposes.
00032 from __future__ import print_function
00033 
00034 import threading
00035 import time
00036 import uuid
00037 
00038 import roslib; roslib.load_manifest('test_bond')
00039 import rospy
00040 from bond.msg import Constants, Status
00041 from test_bond.srv import TestBond, TestBondResponse
00042 
00043 import atexit
00044 atexit.register(rospy.signal_shutdown, 'exit')
00045 
00046 import BondSM_sm
00047 
00048 
00049 class Timeout:
00050     def __init__(self, duration, on_timeout=None):
00051         self.duration = duration
00052         self.timer = threading.Timer(0, self._on_timer)
00053         self.deadline = rospy.Time.now()
00054         self.on_timeout = on_timeout
00055 
00056     def reset(self):
00057         self.timer.cancel()
00058         self.timer = threading.Timer(self.duration.to_sec(), self._on_timer)
00059         self.timer.start()
00060         self.deadline = rospy.Time.now() + self.duration
00061         return self
00062 
00063     def cancel(self):
00064         self.timer.cancel()
00065 
00066     def left(self):
00067         return max(rospy.Duration(0), self.deadline - rospy.Time.now())
00068 
00069     def _on_timer(self):
00070         if self.on_timeout:
00071             self.on_timeout()
00072 
00073 
00074 class BondTester:
00075     def __init__(self, req):
00076         self.req = req
00077         self.topic = req.topic
00078         self.id = req.id
00079         self.instance_id = str(uuid.uuid4())
00080         self.on_death = None
00081         self.on_life = None
00082         self.is_shutdown = False
00083         self.sister_died_first = False
00084         self.death_started = None
00085 
00086         self.sm = BondSM_sm.BondSM_sm(self)
00087         # self.sm.setDebugFlag(True)
00088         self.lock = threading.RLock()
00089         self.condition = threading.Condition(self.lock)
00090 
00091         self.connect_timeout = Timeout(
00092             rospy.Duration(Constants.DEFAULT_CONNECT_TIMEOUT), self._on_connect_timeout)
00093         self.heartbeat_timeout = Timeout(
00094             rospy.Duration(Constants.DEFAULT_HEARTBEAT_TIMEOUT), self._on_heartbeat_timeout)
00095         self.disconnect_timeout = Timeout(
00096             rospy.Duration(Constants.DEFAULT_DISCONNECT_TIMEOUT), self._on_disconnect_timeout)
00097 
00098         self.connect_timeout.reset()
00099 
00100         self.sub = rospy.Subscriber(self.topic, Status, self._on_bond_status)
00101         self.pub = rospy.Publisher(self.topic, Status)
00102 
00103         self.thread = threading.Thread(target=self._publishing_thread)
00104         self.thread.daemon = True
00105         self.thread.start()
00106 
00107         if req.delay_death >= rospy.Duration(0.0):
00108             self.death_timeout = Timeout(req.delay_death, self.die).reset()
00109 
00110         self.deadline = None
00111 
00112     def _on_connect_timeout(self):
00113         with self.lock:
00114             self.sm.ConnectTimeout()
00115 
00116     def _on_heartbeat_timeout(self):
00117         if self.req.inhibit_death:
00118             return
00119         with self.lock:
00120             self.sm.HeartbeatTimeout()
00121 
00122     def _on_disconnect_timeout(self):
00123         with self.lock:
00124             self.sm.DisconnectTimeout()
00125 
00126     def __del__(self):
00127         self.shutdown()
00128 
00129     def shutdown(self):
00130         if not self.is_shutdown:
00131             with self.lock:
00132                 self.is_shutdown = True
00133                 if self.sm.getState().getName() != 'SM.Dead':
00134                     print("I'm not dead yet:%s in %s" % (self.id, self.sm.getState().getName()))
00135                     self.die()
00136                 self.sub.unregister()
00137                 self.pub.unregister()
00138                 self.condition.notify_all()
00139                 print("Unregistered")
00140 
00141     def _on_bond_status(self, msg):
00142         # Filters out messages from other bonds and messages from ourselves
00143         if msg.id == self.id and msg.instance_id != self.instance_id:
00144             with self.lock:
00145                 if msg.active:
00146                     if not self.is_dead():
00147                         self.sm.SisterAlive()
00148                 else:
00149                     if self.req.inhibit_death:
00150                         return
00151 
00152                     self.sm.SisterDead()
00153 
00154                     # Immediate ack for sister's death notification
00155                     if self.sister_died_first:
00156                         self._publish(False)
00157 
00158     def _publish(self, active):
00159         msg = Status()
00160         msg.header.stamp = rospy.Time.now()
00161         msg.id = self.id
00162         msg.instance_id = self.instance_id
00163         msg.active = active
00164 
00165         if not msg.active and self.req.inhibit_death_message:
00166             pass
00167         else:
00168             self.pub.publish(msg)
00169 
00170     def _publishing_thread(self):
00171 
00172         time.sleep(self.req.delay_connect.to_sec())
00173 
00174         with self.lock:
00175             # Publishing ALIVE
00176             while not self.is_shutdown and self.sm.getState().getName() in [
00177                     'SM.WaitingForSister', 'SM.Alive']:
00178                 self._publish(True)
00179                 self.condition.wait(Constants.DEFAULT_HEARTBEAT_PERIOD)
00180 
00181             # Publishing DEAD
00182             while not self.is_shutdown and self.sm.getState().getName() == 'SM.AwaitSisterDeath':
00183                 self._publish(False)
00184                 self.condition.wait(Constants.DEAD_PUBLISH_PERIOD)
00185 
00186     def Connected(self):
00187         self.connect_timeout.cancel()
00188         self.condition.notify_all()
00189         if self.on_life:
00190             self.on_life()
00191 
00192     def Heartbeat(self):
00193         self.heartbeat_timeout.reset()
00194 
00195     def SisterDied(self):
00196         self.sister_died_first = True
00197 
00198     def Death(self):
00199         self.condition.notify_all()
00200         self.heartbeat_timeout.cancel()
00201         self.disconnect_timeout.cancel()
00202         if not self.death_started:
00203             self.death_started = rospy.Time.now()
00204 
00205     def StartDying(self):
00206         self.heartbeat_timeout.cancel()
00207         self.disconnect_timeout.reset()
00208         if not self.death_started:
00209             self.death_started = rospy.Time.now()
00210 
00211     def wait_for_life(self, timeout=None):
00212         if self.deadline:
00213             self.deadline.cancel()
00214             self.deadline = None
00215         if timeout:
00216             self.deadline = Timeout(timeout).reset()
00217         with self.lock:
00218             while self.sm.getState().getName() == 'SM.WaitingForSister':
00219                 if self.deadline and self.deadline.left() == rospy.Duration(0):
00220                     break
00221                 self.condition.wait(self.deadline and self.deadline.left().to_sec())
00222             return self.sm.getState().getName() != 'SM.WaitingForSister'
00223 
00224     def wait_for_death(self, timeout=None):
00225         if self.deadline:
00226             self.deadline.cancel()
00227             self.deadline = None
00228         if timeout:
00229             self.deadline = Timeout(timeout).reset()
00230         with self.lock:
00231             while self.sm.getState().getName() != 'SM.Dead':
00232                 if self.deadline and self.deadline.left() == rospy.Duration(0):
00233                     break
00234                 self.condition.wait(self.deadline and self.deadline.left().to_sec())
00235             return self.sm.getState().getName() == 'SM.Dead'
00236 
00237     def is_dead(self):
00238         with self.lock:
00239             return self.sm.getState().getName() == 'SM.Dead'
00240 
00241     def die(self):
00242         with self.lock:
00243             self.sm.Die()
00244             self._publish(False)
00245 
00246     def __repr__(self):
00247         return "[Bond %s, Instance %s (%s)]" % (
00248             self.id, self.instance_id, self.sm.getState().getName())
00249 
00250 
00251 class Tester:
00252     def __init__(self):
00253         self.bond_tester = None
00254         self.service = rospy.Service('test_bond', TestBond, self._test_bond)
00255 
00256     def _test_bond(self, req):
00257         print("TEST")
00258         if self.bond_tester:
00259             self.bond_tester.shutdown()
00260         self.bond_tester = BondTester(req)
00261         print("Test bond instance id: %s" % self.bond_tester.instance_id)
00262         return TestBondResponse()
00263 
00264 
00265 def main():
00266     rospy.init_node('bond_tester', anonymous=True, disable_signals=True)
00267     tester = Tester()  # noqa
00268     rospy.spin()
00269 
00270 
00271 if __name__ == '__main__':
00272     main()


test_bond
Author(s): Stuart Glaser
autogenerated on Thu Jun 6 2019 20:40:43