$search
00001 #! /usr/bin/python 00002 #*********************************************************** 00003 #* Software License Agreement (BSD License) 00004 #* 00005 #* Copyright (c) 2009, Willow Garage, Inc. 00006 #* All rights reserved. 00007 #* 00008 #* Redistribution and use in source and binary forms, with or without 00009 #* modification, are permitted provided that the following conditions 00010 #* are met: 00011 #* 00012 #* * Redistributions of source code must retain the above copyright 00013 #* notice, this list of conditions and the following disclaimer. 00014 #* * Redistributions in binary form must reproduce the above 00015 #* copyright notice, this list of conditions and the following 00016 #* disclaimer in the documentation and/or other materials provided 00017 #* with the distribution. 00018 #* * Neither the name of Willow Garage, Inc. nor the names of its 00019 #* contributors may be used to endorse or promote products derived 00020 #* from this software without specific prior written permission. 00021 #* 00022 #* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00023 #* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00024 #* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00025 #* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00026 #* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00027 #* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00028 #* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00029 #* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00030 #* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 #* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00032 #* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00033 #* POSSIBILITY OF SUCH DAMAGE. 00034 #* 00035 #* Author: Eitan Marder-Eppstein 00036 #*********************************************************** 00037 from __future__ import with_statement 00038 00039 PKG='continuous_ops_alerts' 00040 import roslib; roslib.load_manifest(PKG) 00041 00042 import rospy 00043 from googlevoice import Voice 00044 from googlevoice.util import input 00045 00046 import tf2 00047 import tf2_ros 00048 00049 import math 00050 00051 from geometry_msgs.msg import PoseStamped 00052 from pr2_msgs.msg import PowerState 00053 from std_msgs.msg import Bool 00054 import tf2_geometry_msgs 00055 00056 import threading 00057 00058 class ContinuousOpsMonitor: 00059 def __init__(self, voice): 00060 self.voice = voice 00061 self.tf_client = tf2_ros.BufferClient('tf2_buffer_server') 00062 rospy.loginfo('Continuous ops monitor waiting for tf server') 00063 self.tf_client.wait_for_server() 00064 rospy.loginfo('Continuous ops monitor connected to tf server') 00065 00066 self.move_distance = rospy.get_param('~move_distance', 2.0) 00067 self.move_timeout = rospy.get_param('~move_timeout', 300.0) 00068 #self.phone_numbers = ['8472745914', '6505294522', '6263729726'] 00069 self.phone_numbers = ['5107100513', '6505294522'] 00070 00071 self.last_pose = PoseStamped() 00072 self.last_pose.header.stamp = rospy.Time.now() 00073 00074 self.mutex = threading.Lock() 00075 self.plugged_in = True 00076 00077 self.power_sub = rospy.Subscriber('power_state', PowerState, self.power_cb) 00078 self.motor_sub = rospy.Subscriber('pr2_etherCAT/motors_halted', Bool, self.motor_cb) 00079 self.motors_halted = False 00080 00081 00082 def motor_cb(self, msg): 00083 with self.mutex: 00084 if msg.data: 00085 if not self.motors_halted: 00086 self.send_sms('My leg hurts, by casters suck, my motors halted... ') 00087 self.motors_halted = True 00088 else: 00089 self.motors_halted = False 00090 00091 def power_cb(self, msg): 00092 with self.mutex: 00093 if msg.AC_present == 0: 00094 if self.plugged_in: 00095 self.last_pose.header.stamp = rospy.Time.now() 00096 self.plugged_in = False 00097 00098 else: 00099 self.plugged_in = True 00100 00101 def distance(self, p1, p2): 00102 dx = p1.pose.position.x - p2.pose.position.x 00103 dy = p1.pose.position.y - p2.pose.position.y 00104 distance = math.sqrt(dx * dx + dy * dy) 00105 return distance 00106 00107 def distance_check(self): 00108 base_pose = PoseStamped() 00109 base_pose.header.stamp = rospy.Time(0.0) 00110 base_pose.header.frame_id = 'base_link' 00111 base_pose.pose.orientation.w = 1.0 00112 00113 try: 00114 map_pose = self.tf_client.transform(base_pose, 'map') 00115 dist = self.distance(map_pose, self.last_pose) 00116 00117 #If we've moved far enough, then we'll update our last pose and return 00118 if dist > self.move_distance: 00119 self.last_pose = map_pose 00120 return 00121 00122 #If we haven't moved far enough, we need to check if we've timed out 00123 if self.last_pose.header.stamp + rospy.Duration(self.move_timeout) < rospy.Time.now(): 00124 self.send_sms('Robot has not moved in %s seconds, please check if it needs help.' % self.move_timeout) 00125 #Don't send too many sms messages, we'll reset our timer 00126 self.last_pose.header.stamp = rospy.Time.now() 00127 00128 except tf2.TransformException as e: 00129 rospy.logerr("%s" % e) 00130 00131 def send_sms(self, text): 00132 for number in self.phone_numbers: 00133 self.voice.send_sms(number, text) 00134 00135 def run(self): 00136 r = rospy.Rate(0.1) 00137 while not rospy.is_shutdown(): 00138 with self.mutex: 00139 if not self.plugged_in: 00140 #check that things are OK here 00141 self.distance_check() 00142 r.sleep() 00143 00144 if __name__ == '__main__': 00145 rospy.init_node('continuous_ops_alert') 00146 voice = Voice() 00147 voice.login() 00148 com = ContinuousOpsMonitor(voice) 00149 00150 try: 00151 com.run() 00152 except rospy.ROSInterruptException: pass 00153