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 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 import rospy
00043 
00044 import dbus
00045 from dbus.exceptions import DBusException
00046 
00047 from naoqi_driver.naoqi_node import NaoqiNode
00048 import os
00049 
00050 from diagnostic_msgs.msg import *
00051 
00052 class NaoDiagnosticUpdater(NaoqiNode):
00053     def __init__(self):
00054         NaoqiNode.__init__(self, 'nao_diagnostic_updater')
00055 
00056         
00057         self.connectNaoQi()
00058 
00059         
00060         self.sleep = 1.0/rospy.get_param('~update_rate', 0.5)  
00061         self.warningThreshold = rospy.get_param('~warning_threshold', 68) 
00062         self.errorThreshold = rospy.get_param('~error_threshold', 74)     
00063         
00064         self.runsOnRobot = "aldebaran" in os.uname()[2]   
00065 
00066         
00067         self.jointNamesList = self.motionProxy.getJointNames('Body')
00068         self.jointTempPathsList = []
00069         self.jointStiffPathsList = []
00070         for i in range(0, len(self.jointNamesList)):
00071             self.jointTempPathsList.append("Device/SubDeviceList/%s/Temperature/Sensor/Value" % self.jointNamesList[i])
00072             self.jointStiffPathsList.append("Device/SubDeviceList/%s/Hardness/Actuator/Value" % self.jointNamesList[i])
00073 
00074         self.batteryNamesList = ["Percentage", "Status"]
00075         self.batteryPathsList = ["Device/SubDeviceList/Battery/Charge/Sensor/Value",
00076                                  "Device/SubDeviceList/Battery/Charge/Sensor/Status",
00077                                  "Device/SubDeviceList/Battery/Current/Sensor/Value"]
00078 
00079         self.deviceInfoList = ["Device/DeviceList/ChestBoard/BodyId"]
00080         deviceInfoData = self.memProxy.getListData(self.deviceInfoList)
00081         if(len(deviceInfoData) > 1 and isinstance(deviceInfoData[1], list)):
00082             deviceInfoData = deviceInfoData[1]
00083         if(deviceInfoData[0] is None or deviceInfoData[0] == 0):
00084             
00085             self.isSimulator = True
00086             if(self.pip.startswith("127.") or self.pip == "localhost"):
00087                 
00088                 f = open("/etc/hostname")
00089                 self.hardwareID = "%s:%d"%(f.readline().rstrip(), self.pport)
00090                 f.close()
00091             else:
00092                 self.hardwareID = "%s:%d"%(self.pip, self.pport)
00093         else:
00094             self.hardwareID = deviceInfoData[0].rstrip()
00095             self.isSimulator = False
00096 
00097         
00098         self.diagnosticPub = rospy.Publisher("diagnostics", DiagnosticArray)
00099 
00100         rospy.loginfo("nao_diagnostic_updater initialized")
00101 
00102     
00103     def connectNaoQi(self):
00104         """ Connects to NaoQi and gets proxies to ALMotion and ALMemory. """
00105         rospy.loginfo("Connecting to NaoQi at %s:%d", self.pip, self.pport)
00106         self.motionProxy = self.get_proxy("ALMotion")
00107         self.memProxy = self.get_proxy("ALMemory")
00108         if self.motionProxy is None or self.memProxy is None:
00109             exit(1)
00110 
00111     def run(self):
00112         """ Diagnostic thread code - collects and sends out diagnostic data. """
00113         while self.is_looping():
00114             try:
00115                 
00116                 jointsTempData = self.memProxy.getListData(self.jointTempPathsList)
00117                 jointsStiffData = self.memProxy.getListData(self.jointStiffPathsList)
00118                 batteryData = self.memProxy.getListData(self.batteryPathsList)
00119                 if isinstance(jointsTempData[1], list):
00120                     
00121                     
00122                     
00123                     jointsTempData = jointsTempData[1]
00124                     jointsStiffData = jointsStiffData[1]
00125                     batteryData = batteryData[1]
00126             except RuntimeError,e:
00127                 print "Error accessing ALMemory, exiting...\n"
00128                 print e
00129                 rospy.signal_shutdown("No NaoQI available anymore")
00130 
00131             diagnosticArray = DiagnosticArray()
00132 
00133             
00134             for i in range(0, len(self.jointTempPathsList)):
00135                 status = DiagnosticStatus()
00136                 status.hardware_id = "%s#%s"%(self.hardwareID, self.jointNamesList[i])
00137                 status.name = "nao_joint: %s" % self.jointNamesList[i]
00138                 kv = KeyValue()
00139                 kv.key = "Temperature"
00140                 temperature = jointsTempData[i]
00141                 kv.value = str(temperature)
00142                 status.values.append(kv)
00143                 kv = KeyValue()
00144                 kv.key = "Stiffness"
00145                 if self.jointNamesList[i] == "RHipYawPitch":
00146                     
00147                     kv.value = "None"
00148                 else:
00149                     kv.value = str(jointsStiffData[i])
00150                 status.values.append(kv)
00151                 if (type(temperature) != float and type(temperature) != int) or self.jointNamesList[i] == "RHipYawPitch":
00152                     status.level = DiagnosticStatus.OK
00153                     status.message = "No temperature sensor"
00154                 elif temperature < self.warningThreshold:
00155                     status.level = DiagnosticStatus.OK
00156                     status.message = "Normal: %3.1f C" % temperature
00157                 elif temperature < self.errorThreshold:
00158                     status.level = DiagnosticStatus.WARN
00159                     status.message = "Hot: %3.1f C" % temperature
00160                 else:
00161                     status.level = DiagnosticStatus.ERROR
00162                     status.message = "Too hot: %3.1f C" % temperature
00163                 diagnosticArray.status.append(status)
00164 
00165             
00166             status = DiagnosticStatus()
00167             status.hardware_id = "%s#%s"%(self.hardwareID, "battery")
00168             status.name ="nao_power: Battery"
00169             kv = KeyValue()
00170             kv.key = "Percentage"
00171             if batteryData[0] is None:
00172                 kv.value = "unknown"
00173             else:
00174                 kv.value = str(batteryData[0] * 100)
00175             status.values.append(kv)
00176 
00177             
00178             
00179             statuskeys = ["End off Discharge flag", "Near End Off Discharge flag", "Charge FET on", "Discharge FET on", "Accu learn flag", "Discharging flag", "Full Charge Flag", "Charge Flag", "Charge Temperature Alarm", "Over Charge Alarm", "Discharge Alarm", "Charge Over Current Alarm", "Discharge Over Current Alarm (14A)", "Discharge Over Current Alarm (6A)", "Discharge Temperature Alarm", "Power-Supply present" ]
00180 
00181             for j in range(0, 16):
00182                 kv = KeyValue()
00183                 kv.key = statuskeys[j]
00184                 if batteryData[1] is None:
00185                     kv.value = "unknown"
00186                 elif int(batteryData[1]) & 1<<j:
00187                     kv.value = "True"
00188                 else:
00189                     kv.value = "False"
00190                 status.values.append(kv)
00191 
00192             kv = KeyValue()
00193             kv.key = "Status"
00194             if batteryData[1] is None:
00195                 kv.value = "unknown"
00196             else:
00197                 kv.value = bin(int(batteryData[1]))
00198             status.values.append(kv)
00199 
00200             
00201             if batteryData[0] is None:
00202                 status.level = DiagnosticStatus.OK
00203                 status.message = "Battery status unknown, assuming simulation"
00204             elif int(batteryData[1]) & 1<<6:
00205                 status.level = DiagnosticStatus.OK
00206                 status.message = "Battery fully charged"
00207             elif int(batteryData[1]) & 1<<7:
00208                 status.level = DiagnosticStatus.OK
00209                 status.message = "Charging (%3.1f%%)" % (batteryData[0] * 100)
00210             elif batteryData[0] > 0.60:
00211                 status.level = DiagnosticStatus.OK
00212                 status.message = "Battery OK (%3.1f%% left)" % (batteryData[0] * 100)
00213             elif batteryData[0] > 0.30:
00214                 status.level = DiagnosticStatus.WARN
00215                 status.message = "Battery discharging (%3.1f%% left)" % (batteryData[0] * 100)
00216             else:
00217                 status.level = DiagnosticStatus.ERROR
00218                 status.message = "Battery almost empty (%3.1f%% left)" % (batteryData[0] * 100)
00219             diagnosticArray.status.append(status)
00220 
00221             
00222             status = DiagnosticStatus()
00223             status.hardware_id = "%s#%s"%(self.hardwareID, "power")
00224             status.name = "nao_power: Current"
00225 
00226             if batteryData[2] is None:
00227                 status.level = DiagnosticStatus.OK
00228                 status.message = "Total Current: unknown"
00229             else:
00230                 kv = KeyValue()
00231                 kv.key = "Current"
00232                 kv.value = str(batteryData[2])
00233                 status.values.append(kv)
00234                 status.level = DiagnosticStatus.OK
00235                 if batteryData[2] > 0:
00236                     currentStatus = "charging"
00237                 else:
00238                     currentStatus = "discharging"
00239                 status.message = "Total Current: %3.2f Ampere (%s)" % (batteryData[2], currentStatus)
00240             diagnosticArray.status.append(status)
00241 
00242             
00243             status = DiagnosticStatus()
00244             status.hardware_id = "%s#%s"%(self.hardwareID, "cpu")
00245             status.name = "nao_cpu: Head Temperature"
00246             temp = self.temp_get()
00247             kv = KeyValue()
00248             kv.key = "CPU Temperature"
00249             kv.value = str(temp['HeadSilicium'])
00250             status.values.append(kv)
00251             kv = KeyValue()
00252             kv.key = "Board Temperature"
00253             kv.value = str(temp['HeadBoard'])
00254             status.values.append(kv)
00255             if(temp['HeadSilicium'] == "invalid"):
00256                 status.level = DiagnosticStatus.OK
00257                 status.message = "unknown, assuming simulation"
00258             else:
00259                 status.message = "%3.2f deg C" % (temp['HeadSilicium'])
00260                 if temp['HeadSilicium'] < 100:
00261                     status.level = DiagnosticStatus.OK
00262                 elif temp['HeadSilicium'] < 105:
00263                     status.level = DiagnosticStatus.WARN
00264                 else:
00265                     status.level = DiagnosticStatus.ERROR
00266             diagnosticArray.status.append(status)
00267 
00268             
00269             statusWifi = DiagnosticStatus()
00270             statusWifi.hardware_id = "%s#%s"%(self.hardwareID, "wlan")
00271             statusWifi.name = "nao_wlan: Status"
00272 
00273             statusLan = DiagnosticStatus()
00274             statusLan.hardware_id = "%s#%s"%(self.hardwareID, "ethernet")
00275             statusLan.name = "nao_ethernet: Status"
00276 
00277             if self.runsOnRobot:
00278                 statusLan.level = DiagnosticStatus.ERROR
00279                 statusLan.message = "offline"
00280                 statusWifi.level = DiagnosticStatus.ERROR
00281                 statusWifi.message = "offline"
00282                 systemBus = dbus.SystemBus()
00283                 try:
00284                     manager = dbus.Interface(systemBus.get_object("org.moblin.connman", "/"), "org.moblin.connman.Manager")
00285                 except DBusException as e:
00286                     self.runsOnRobot = False
00287                 if self.runsOnRobot:
00288                     properties = manager.GetProperties()
00289                     for property in properties["Services"]:
00290                         service = dbus.Interface(systemBus.get_object("org.moblin.connman", property), "org.moblin.connman.Service")
00291                         try:
00292                             props = service.GetProperties()
00293                         except DBusException as e:
00294                             continue 
00295 
00296                         state = str(props.get("State", "None"))
00297                         if state == "idle":
00298                             pass 
00299                         else:
00300                             nettype = str(props.get("Type", "<unknown>"))
00301                             if(nettype == "wifi"):
00302                                 status = statusWifi
00303                             else:
00304                                 status = statusLan
00305                             kv = KeyValue()
00306                             kv.key = "Network"
00307                             kv.value = str(props.get("Name", "<unknown>"))
00308                             status.values.append(kv)
00309                             if nettype == "wifi":
00310                                 strength = int(props.get("Strength", "<unknown>"))
00311                                 kv = KeyValue()
00312                                 kv.key = "Strength"
00313                                 kv.value = "%d%%"%strength
00314                                 status.values.append(kv)
00315                             else:
00316                                 strength = None
00317                             kv = KeyValue()
00318                             kv.key = "Type"
00319                             kv.value = nettype
00320                             status.values.append(kv)
00321                             if strength is None:
00322                                 status.message = state
00323                             else:
00324                                 status.message = "%s (%d%%)"%(state, strength)
00325 
00326                             if state in ["online", "ready"]:
00327                                 status.level = DiagnosticStatus.OK
00328                             elif state in ["configuration", "association"]:
00329                                 status.level = DiagnosticStatus.WARN
00330                             else: 
00331                                 status.message = str("%s (%s)"%(state, props.get("Error")))
00332                                 status.level = DiagnosticStatus.ERROR
00333             else:
00334                 statusWifi.level = DiagnosticStatus.OK
00335                 statusWifi.message = "nao_diagnostic_updater not running on robot, cannot determine WLAN status"
00336 
00337                 statusLan.level = DiagnosticStatus.OK
00338                 statusLan.message = "nao_diagnostic_updater not running on robot, cannot determine Ethernet status"
00339 
00340             diagnosticArray.status.append(statusWifi)
00341             diagnosticArray.status.append(statusLan)
00342 
00343             
00344             diagnosticArray.header.stamp = rospy.Time.now()
00345             self.diagnosticPub.publish(diagnosticArray)
00346             rospy.sleep(self.sleep)
00347 
00348     def temp_get(self):
00349         """Read the CPU and head temperature from the system devices.
00350 
00351         Returns {'HeadSilicium': <temperature>, 'HeadBoard': <temperature>}
00352 
00353         Temperatures are returned as float values in degrees Celsius, or
00354         as the string 'invalid' if the sensors are not accessible.
00355         """
00356         temp = {'HeadSilicium': 'invalid', 'HeadBoard': 'invalid'}
00357         if(self.runsOnRobot):
00358             try:
00359                 f = open("/sys/class/i2c-adapter/i2c-1/1-004c/temp2_input")
00360                 temp['HeadSilicium'] = float(f.readline()) / 1000.
00361                 f.close()
00362             except IOError:
00363                 self.runsOnRobot = False
00364                 return temp
00365             except:
00366                 temp['HeadSilicium'] = "invalid"
00367             try:
00368                 f = open("/sys/class/i2c-adapter/i2c-1/1-004c/temp1_input")
00369                 temp['HeadBoard'] = float(f.readline()) / 1000.
00370                 f.close()
00371             except IOError:
00372                 self.runsOnRobot = False
00373                 return temp
00374             except:
00375                 temp['HeadBoard'] = "invalid"
00376         return temp
00377 
00378 
00379 if __name__ == '__main__':
00380 
00381     updater = NaoDiagnosticUpdater()
00382     updater.start()
00383 
00384     rospy.spin()
00385     exit(0)