Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 import threading
00034 import time
00035 import uuid
00036 
00037 import roslib; roslib.load_manifest('test_bond')
00038 import rospy
00039 from bond.msg import *
00040 from test_bond.srv import *
00041 
00042 import atexit
00043 atexit.register(rospy.signal_shutdown, 'exit')
00044 
00045 import BondSM_sm
00046 
00047 class Timeout:
00048     def __init__(self, duration, on_timeout=None):
00049         self.duration = duration
00050         self.timer = threading.Timer(0, self._on_timer)
00051         self.deadline = rospy.Time.now()
00052         self.on_timeout = on_timeout
00053 
00054     def reset(self):
00055         self.timer.cancel()
00056         self.timer = threading.Timer(self.duration.to_sec(), self._on_timer)
00057         self.timer.start()
00058         self.deadline = rospy.Time.now() + self.duration
00059         return self
00060 
00061     def cancel(self):
00062         self.timer.cancel()
00063 
00064     def left(self):
00065         return max(rospy.Duration(0), self.deadline - rospy.Time.now())
00066 
00067     def _on_timer(self):
00068         if self.on_timeout:
00069             self.on_timeout()
00070 
00071 
00072 class BondTester:
00073     def __init__(self, req):
00074         self.req = req
00075         self.topic = req.topic
00076         self.id = req.id
00077         self.instance_id = str(uuid.uuid4())
00078         self.on_death = None
00079         self.on_life = None
00080         self.is_shutdown = False
00081         self.sister_died_first = False
00082         self.death_started = None
00083 
00084         self.sm = BondSM_sm.BondSM_sm(self)
00085         
00086         self.lock = threading.RLock()
00087         self.condition = threading.Condition(self.lock)
00088 
00089         self.connect_timeout = Timeout(rospy.Duration(Constants.DEFAULT_CONNECT_TIMEOUT), self._on_connect_timeout)
00090         self.heartbeat_timeout = Timeout(rospy.Duration(Constants.DEFAULT_HEARTBEAT_TIMEOUT), self._on_heartbeat_timeout)
00091         self.disconnect_timeout = Timeout(rospy.Duration(Constants.DEFAULT_DISCONNECT_TIMEOUT), self._on_disconnect_timeout)
00092 
00093         self.connect_timeout.reset()
00094 
00095         self.sub = rospy.Subscriber(self.topic, Status, self._on_bond_status)
00096         self.pub = rospy.Publisher(self.topic, Status)
00097 
00098         self.thread = threading.Thread(target=self._publishing_thread)
00099         self.thread.daemon = True
00100         self.thread.start()
00101 
00102         if req.delay_death >= rospy.Duration(0.0):
00103             self.death_timeout = Timeout(req.delay_death, self.die).reset()
00104 
00105     def _on_connect_timeout(self):
00106         with self.lock:
00107             self.sm.ConnectTimeout()
00108     def _on_heartbeat_timeout(self):
00109         if self.req.inhibit_death:
00110             return
00111         with self.lock:
00112             self.sm.HeartbeatTimeout()
00113     def _on_disconnect_timeout(self):
00114         with self.lock:
00115             self.sm.DisconnectTimeout()
00116 
00117     def __del__(self):
00118         self.shutdown()
00119 
00120     def shutdown(self):
00121         if not self.is_shutdown:
00122             with self.lock:
00123                 self.is_shutdown = True
00124                 if self.sm.getState().getName() != 'SM.Dead':
00125                     print "I'm not dead yet:", self.id, " in ", self.sm.getState().getName()
00126                     self.die()
00127                 self.sub.unregister()
00128                 self.pub.unregister()
00129                 self.condition.notify_all()
00130                 print "Unregistered"
00131 
00132     def _on_bond_status(self, msg):
00133         
00134         if msg.id == self.id and msg.instance_id != self.instance_id:
00135             with self.lock:
00136                 if msg.active:
00137                     if not self.is_dead():
00138                         self.sm.SisterAlive()
00139                 else:
00140                     if self.req.inhibit_death:
00141                         return
00142 
00143                     self.sm.SisterDead()
00144 
00145                     
00146                     if self.sister_died_first:
00147                         self._publish(False)
00148 
00149 
00150     def _publish(self, active):
00151         msg = Status()
00152         msg.header.stamp = rospy.Time.now()
00153         msg.id = self.id
00154         msg.instance_id = self.instance_id
00155         msg.active = active
00156 
00157         if not msg.active and self.req.inhibit_death_message:
00158             pass
00159         else:
00160             self.pub.publish(msg)
00161 
00162     def _publishing_thread(self):
00163 
00164         time.sleep(self.req.delay_connect.to_sec())
00165 
00166         with self.lock:
00167             
00168             while not self.is_shutdown and self.sm.getState().getName() in ['SM.WaitingForSister', 'SM.Alive']:
00169                 self._publish(True)
00170                 self.condition.wait(Constants.DEFAULT_HEARTBEAT_PERIOD)
00171 
00172             
00173             while not self.is_shutdown and self.sm.getState().getName() == 'SM.AwaitSisterDeath':
00174                 self._publish(False)
00175                 self.condition.wait(Constants.DEAD_PUBLISH_PERIOD)
00176 
00177 
00178     def Connected(self):
00179         self.connect_timeout.cancel()
00180         self.condition.notify_all()
00181         if self.on_life:
00182             self.on_life()
00183 
00184     def Heartbeat(self):
00185         self.heartbeat_timeout.reset()
00186 
00187     def SisterDied(self):
00188         self.sister_died_first = True
00189 
00190     def Death(self):
00191         self.condition.notify_all()
00192         self.heartbeat_timeout.cancel()
00193         self.disconnect_timeout.cancel()
00194         if not self.death_started:
00195             self.death_started = rospy.Time.now()
00196 
00197     def StartDying(self):
00198         self.heartbeat_timeout.cancel()
00199         self.disconnect_timeout.reset()
00200         if not self.death_started:
00201             self.death_started = rospy.Time.now()
00202 
00203     def wait_for_life(self, timeout = None):
00204         deadline = timeout and Timeout(timeout).reset()
00205         with self.lock:
00206             while self.sm.getState().getName() == 'SM.WaitingForSister':
00207                 if deadline and deadline.left() == rospy.Duration(0):
00208                     break
00209                 self.condition.wait(deadline and deadline.left().to_sec())
00210             return self.sm.getState().getName() != 'SM.WaitingForSister'
00211 
00212     def wait_for_death(self, timeout = None):
00213         deadline = timeout and Timeout(timeout).reset()
00214         with self.lock:
00215             while self.sm.getState().getName() != 'SM.Dead':
00216                 if deadline and deadline.left() == rospy.Duration(0):
00217                     break
00218                 self.condition.wait(deadline and deadline.left().to_sec())
00219             return self.sm.getState().getName() == 'SM.Dead'
00220 
00221     def is_dead(self):
00222         with self.lock:
00223             return self.sm.getState().getName() == 'SM.Dead'
00224 
00225     def die(self):
00226         with self.lock:
00227             self.sm.Die()
00228             self._publish(False)
00229 
00230     def __repr__(self):
00231         return "[Bond %s, Instance %s (%s)]" % (self.id, self.instance_id, self.sm.getState().getName())
00232 
00233 
00234 class Tester:
00235     def __init__(self):
00236         self.bond_tester = None
00237         self.service = rospy.Service('test_bond', TestBond, self._test_bond)
00238 
00239     def _test_bond(self, req):
00240         print "TEST"
00241         if self.bond_tester:
00242             self.bond_tester.shutdown()
00243         self.bond_tester = BondTester(req)
00244         print "Test bond instance id: %s" % self.bond_tester.instance_id
00245         return TestBondResponse()
00246 
00247 def main():
00248     rospy.init_node('bond_tester', anonymous=True, disable_signals=True)
00249     tester = Tester()
00250     rospy.spin()
00251 
00252 if __name__ == '__main__': main()