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 This tests that dropped etherCAT packets are detected as errors or
00041 ignored depending on late packets. This simulates diagnostics data from
00042 "EtherCAT Master" and checks the output from the test monitor.
00043 
00044 From #4848: If net drops > 10 per hour, this is a failure.
00045 "net drops" = dropped packets - late packets
00046 """
00047 
00048 from __future__ import with_statement
00049 
00050 PKG = 'pr2_hardware_test_monitor'
00051 import roslib; roslib.load_manifest(PKG)
00052 
00053 import unittest
00054 import rospy, rostest
00055 from time import sleep
00056 import sys
00057 
00058 from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus, KeyValue
00059 from pr2_self_test_msgs.msg import TestStatus
00060 from std_msgs.msg import Bool
00061 import std_msgs.msg
00062 from std_srvs.srv import *
00063 import threading
00064 
00065 GRACE_TIME = 35 
00066 IGNORE_TIME = 5
00067 
00068 
00069 def _ecat_diag(pkts = 0, lates = 0):
00070     
00071     if pkts < lates:
00072         pkts = lates
00073 
00074     array = DiagnosticArray()
00075     stat = DiagnosticStatus()
00076     stat.name = 'EtherCAT Master'
00077     stat.level = 0
00078     stat.message = 'OK'
00079     stat.values.append(KeyValue('Dropped Packets', str(pkts)))
00080     stat.values.append(KeyValue('RX Late Packet', str(lates)))
00081 
00082     mcb_stat = DiagnosticStatus()
00083     mcb_stat.name = 'EtherCAT Device (my_motor)'
00084     mcb_stat.level = 0
00085     mcb_stat.values.append(KeyValue('Num encoder_errors', '0'))
00086 
00087     array.header.stamp = rospy.get_rostime()
00088     array.status.append(stat)
00089     array.status.append(mcb_stat)
00090     
00091     return array
00092 
00093 class TestDroppedPacket(unittest.TestCase):
00094     def __init__(self, *args):
00095         super(TestDroppedPacket, self).__init__(*args)
00096 
00097         self._mutex = threading.Lock()
00098         rospy.init_node('test_monitor_drop_pkt')
00099         self._ok = True
00100         self._level = 0
00101 
00102         self._start = rospy.get_time()
00103         self._last_msg = None
00104         self._max_lvl = -1
00105 
00106         self._halted = False
00107 
00108         self._num_drops = rospy.get_param('~num_drops', 10)
00109         self._num_lates = rospy.get_param('~num_lates', 0)
00110         self._increasing_drops = rospy.get_param('~increasing_drops', False)
00111 
00112         self._snapped = False
00113         self._snapshot_sub = rospy.Subscriber('snapshot_trigger', std_msgs.msg.Empty, self._snap_cb)
00114 
00115         self._halt_srv = rospy.Service('pr2_etherCAT/halt_motors', Empty, self.on_halt)
00116         self._diag_pub = rospy.Publisher('/diagnostics', DiagnosticArray)
00117 
00118         self._reset_test = rospy.ServiceProxy('reset_test', Empty)
00119 
00120         rospy.Subscriber('test_status', TestStatus, self._cb)
00121 
00122         self.motors_pub = rospy.Publisher('pr2_etherCAT/motors_halted', Bool)
00123         self.cal_pub = rospy.Publisher('calibrated', Bool, latch=True)
00124 
00125     def on_halt(self, srv):
00126         return EmptyResponse()
00127 
00128 
00129     def _snap_cb(self, msg):
00130         self._snapped = True
00131 
00132     def _cb(self, msg):
00133         with self._mutex:
00134             if not self._last_msg:
00135                 self._start = rospy.get_time()
00136 
00137             self._last_msg = msg
00138 
00139             if rospy.get_time() - self._start > IGNORE_TIME:
00140                 self._max_lvl = max(msg.test_ok, self._max_lvl)
00141 
00142     def test_drop_pkt(self):
00143         self.cal_pub.publish(True)
00144         while not rospy.is_shutdown():
00145             self._diag_pub.publish(_ecat_diag())
00146             self.motors_pub.publish(False)
00147             sleep(1.0)
00148             if rospy.get_time() - self._start > GRACE_TIME:
00149                 break
00150 
00151         
00152         self._diag_pub.publish(_ecat_diag(1))
00153         self.motors_pub.publish(False)
00154         sleep(1.0)
00155         
00156 
00157         
00158         if not self._increasing_drops:
00159             for i in range(0, 10):
00160                 self._diag_pub.publish(_ecat_diag(self._num_drops, self._num_lates))
00161                 self.motors_pub.publish(False)
00162                 sleep(1.0)
00163         else: 
00164             drop_count = 0
00165             late_count = 0
00166             for i in range(0, max(self._num_drops, self._num_lates)):
00167                 if drop_count < self._num_drops:
00168                     drop_count += 1
00169                 if late_count < self._num_lates:
00170                     late_count += 1
00171                 self._diag_pub.publish(_ecat_diag(drop_count, late_count))
00172                 self.motors_pub.publish(False)
00173                 sleep(1.0)
00174 
00175         with self._mutex:
00176             self.assert_(not rospy.is_shutdown(), "Rospy shutdown")
00177 
00178             self.assert_(self._last_msg is not None, "No data from test monitor")
00179             
00180             
00181             if self._num_drops - self._num_lates >= 10:
00182                 self.assert_(self._last_msg.test_ok == TestStatus.ERROR, "Test monitor reports that we're not in error state. Level: %d. Message: %s" % (self._last_msg.test_ok, self._last_msg.message))
00183                 
00184                 self.assert_(self._snapped, "Snapshot trigger wasn't called, but we did halt")
00185             else:
00186                 self.assert_(self._last_msg.test_ok == TestStatus.RUNNING, "Test monitor reports that we're not running. Level: %d. Message: %s" % (self._last_msg.test_ok, self._last_msg.message))
00187                 
00188                 self.assert_(not self._snapped, "Snapshot trigger wasn't called, but we did halt")
00189 
00190 if __name__ == '__main__':
00191     if len(sys.argv) > 1 and sys.argv[1] == '-v':
00192         suite = unittest.TestSuite()
00193         suite.addTest(TestDroppedPacket('test_dropped_pkt'))
00194 
00195         unittest.TextTestRunner(verbosity=2).run(suite)
00196     else:
00197         rostest.run(PKG, sys.argv[0], TestDroppedPacket, sys.argv)