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 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
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
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
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
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
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()
00268 rospy.spin()
00269
00270
00271 if __name__ == '__main__':
00272 main()