warning.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 import rospy
00004 import time, socket, os
00005 from math import fabs
00006 import threading
00007 import re
00008 
00009 from sound_play.libsoundplay import SoundClient
00010 
00011 from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus
00012 from fetch_driver_msgs.msg import RobotState
00013 from geometry_msgs.msg import Twist
00014 from power_msgs.msg import BatteryState, BreakerState
00015 from power_msgs.srv import BreakerCommand,  BreakerCommandRequest
00016 
00017 ## http://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python
00018 import ctypes
00019 def terminate_thread(thread):
00020     """Terminates a python thread from another thread.
00021 
00022     :param thread: a threading.Thread instance
00023     """
00024     if not thread.isAlive():
00025         return
00026 
00027     exc = ctypes.py_object(SystemExit)
00028     res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
00029         ctypes.c_long(thread.ident), exc)
00030     if res == 0:
00031         raise ValueError("nonexistent thread id")
00032     elif res > 1:
00033         # """if it returns a number greater than one, you're in trouble,
00034         # and you should call it again with exc=NULL to revert the effect"""
00035         ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
00036         raise SystemError("PyThreadState_SetAsyncExc failed")
00037 
00038 class DiagnosticsSpeakThread(threading.Thread):
00039     def __init__(self, error_status):
00040         threading.Thread.__init__(self)
00041         self.error_status = error_status
00042         self.start()
00043 
00044     def run(self):
00045         global sound
00046         for status in  self.error_status:
00047             # we can ignore "Joystick not open."
00048             if status.message == "Joystick not open." :
00049                 continue
00050             if status.name == "supply_breaker" and status.message == "Disabled." :
00051                 continue
00052             #
00053             text = "Error on {}, {}".format(status.name, status.message)
00054             rospy.loginfo(text)
00055             text = text.replace('_', ', ')
00056             sound.say(text, 'voice_kal_diphone')
00057             time.sleep(5)
00058 
00059     def stop(self):
00060         terminate_thread(self)
00061         self.join()
00062 
00063 class Warning:
00064     def __init__(self):
00065         time.sleep(1)
00066         self.battery_sub = rospy.Subscriber("battery_state", BatteryState, self.battery_callback, queue_size = 1)
00067         self.cmd_vel_sub = rospy.Subscriber("base_controller/command", Twist, self.cmd_vel_callback, queue_size = 1)
00068         self.robot_state_sub = rospy.Subscriber("robot_state", RobotState, self.robot_state_callback, queue_size = 1)
00069         self.diagnostics_status_sub = rospy.Subscriber("diagnostics", DiagnosticArray, self.diagnostics_status_callback, queue_size = 1)
00070         self.base_breaker = rospy.ServiceProxy('base_breaker', BreakerCommand)
00071         #
00072         self.robot_state_msgs = RobotState()
00073         self.battery_state_msgs = BatteryState()
00074         self.twist_msgs = Twist()
00075         #
00076         self.diagnostics_speak_thread = {}
00077 
00078     def robot_state_callback(self, msg):
00079         self.robot_state_msgs = msg
00080 
00081     def battery_callback(self, msg):
00082         self.battery_state_msgs = msg
00083         #
00084         if msg.is_charging == False and msg.charge_level < 0.1:
00085             sound.play(2)
00086             time.sleep(2)
00087             sound.play(5)
00088             time.sleep(5)
00089 
00090     def cmd_vel_callback(self, msg):
00091         ## warn when cmd_vel is issued while the robot is charning
00092         rospy.logdebug("cmd_vel : x:{} y:{} z:{}, battery.is_charning {}".format(msg.linear.x,msg.linear.y,msg.angular.z,self.battery_state_msgs.is_charging))
00093         breaker_enabled = True
00094         try:
00095             breaker_status = filter(lambda n: n.name=='base_breaker', self.robot_state_msgs.breakers)[0]
00096             breaker_enabled = breaker_status.state == BreakerState.STATE_ENABLED
00097         except Exception as e:
00098             rospy.logerr("Failed to fetch breaker status: %s" % str(e))
00099 
00100         if ( fabs(msg.linear.x) > 0 or fabs(msg.linear.y) > 0 or fabs(msg.angular.z) > 0 ) and \
00101            self.battery_state_msgs.is_charging == True and breaker_enabled:
00102             rospy.logerr("Try to run while charging!")
00103             self.base_breaker(BreakerCommandRequest(enable=False))
00104             sound.play(4) # play builtin sound Boom!
00105             time.sleep(5)
00106             self.base_breaker(BreakerCommandRequest(enable=True))
00107         ##
00108         self.twist_msgs = msg
00109 
00110     def diagnostics_status_callback(self, msg):
00111         ##
00112         self.diagnostic_status_msgs = msg.status
00113         ##
00114         ## check if this comes from /robot_driver
00115         callerid = msg._connection_header['callerid']
00116         if not self.diagnostics_speak_thread.has_key(callerid):
00117             self.diagnostics_speak_thread[callerid] = None
00118         error_status = filter(lambda n: n.level in [DiagnosticStatus.WARN, DiagnosticStatus.ERROR, DiagnosticStatus.STALE], msg.status)
00119         # when RunStopped, ignore message from *_mcb and *_breaker
00120         if self.robot_state_msgs.runstopped:
00121             error_status = filter(lambda n: not (re.match("\w*_(mcb|breaker)",n.name) or (n.name == "Mainboard" and n.message == "Runstop pressed")), error_status)
00122         if not error_status : # error_status is not []
00123             if self.diagnostics_speak_thread[callerid] and self.diagnostics_speak_thread[callerid].is_alive():
00124                 self.diagnostics_speak_thread[callerid].stop()
00125             return
00126         # make sure that diagnostics_speak_thread is None, when the thread is terminated
00127         if self.diagnostics_speak_thread[callerid] and not self.diagnostics_speak_thread[callerid].is_alive():
00128             self.diagnostics_speak_thread[callerid] = None
00129         # run new thread
00130         if self.diagnostics_speak_thread[callerid] is None:
00131             self.diagnostics_speak_thread[callerid] = DiagnosticsSpeakThread(error_status)
00132 
00133 if __name__ == "__main__":
00134     global sound
00135     rospy.init_node("cable_warning")
00136     sound = SoundClient()
00137     w = Warning()
00138     rospy.spin()
00139 
00140 
00141 
00142 


jsk_fetch_startup
Author(s):
autogenerated on Sat Jul 1 2017 02:42:26