io_test.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # ####################################################################
00004 # Copyright (c) 2013, Shadow Robot Company, All rights reserved.
00005 #
00006 # This library is free software; you can redistribute it and/or
00007 # modify it under the terms of the GNU Lesser General Public
00008 # License as published by the Free Software Foundation; either
00009 # version 3.0 of the License, or (at your option) any later version.
00010 #
00011 # This library is distributed in the hope that it will be useful,
00012 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00014 # Lesser General Public License for more details.
00015 #
00016 # You should have received a copy of the GNU Lesser General Public
00017 # License along with this library.
00018 # ####################################################################
00019 
00020 import roslib
00021 import rospy
00022 import sys
00023 import getopt
00024 
00025 from threading import Lock
00026 from sr_ronex_msgs.msg import BoolArray
00027 from std_msgs.msg import UInt16MultiArray
00028 
00029 roslib.load_manifest('sr_ronex_drivers')
00030 
00031 PRODUCT_CODE = "0x05300424"
00032 DIGITAL_IO_WAIT = 0.5
00033 ANALOG_IO_WAIT = 0.1
00034 PWM_O_WAIT = 3.0
00035 ANALOG_RATIO_UPPER = 1.4
00036 ANALOG_RATIO_LOWER = 1.0
00037 PERIOD_ALPHA = 0.01
00038 MAX_PWM_PERIOD = 0.008
00039 MIN_PWM_PERIOD = 0.005
00040 
00041 
00042 class IoTest(object):
00043     """
00044     A class used to test the Shadow Robot ethercat board HW.
00045     For the PRODUCT_CODE = "0x05300424"
00046     4 digital inputs/outputs/PWM outputs
00047     4 analog inputs
00048     2 analog outputs
00049 
00050     The digital outputs will be tested by turning on and off LEDs attached to them.
00051     The digital inputs will read back the value we set for the outputs.
00052     The PWM outputs will be set to their lower frequency, and the digital inputs used to check the on-off period
00053     The analog output 0 will be wired to analog inputs 0 and 2, output 1 to inputs 1 and 3
00054 
00055     Keep in mind that the test is intended to be run with the LEDs attached to the digital I/Os and the correct wiring
00056     of the analog I and O
00057     """
00058 # Example of existing topics for a certain device
00059 # /device_0x05300424_0x00000016_PWM_outputs_command
00060 # /device_0x05300424_0x00000016_analog_inputs_state
00061 # /device_0x05300424_0x00000016_analog_outputs_command
00062 # /device_0x05300424_0x00000016_digital_inputs_state
00063 # /device_0x05300424_0x00000016_digital_outputs_command
00064 
00065     def __init__(self, device_SN):
00066         rospy.loginfo("Testing device. Product code: " + PRODUCT_CODE + " SN: " + device_SN)
00067         self.success = True
00068         self.PWM_testing = False
00069         self.device_SN = device_SN
00070         self.a_state_lock = Lock()
00071         self.d_state_lock = Lock()
00072         self.analog_command_publisher = rospy.Publisher("/device_" + PRODUCT_CODE + "_" + device_SN +
00073                                                         "_analog_outputs_command", UInt16MultiArray, latch=True)
00074         self.digital_command_publisher = rospy.Publisher("/device_" + PRODUCT_CODE + "_" + device_SN +
00075                                                          "_digital_outputs_command", BoolArray, latch=True)
00076         self.PWM_command_publisher = rospy.Publisher("/device_" + PRODUCT_CODE + "_" + device_SN +
00077                                                      "_PWM_outputs_command", UInt16MultiArray, latch=True)
00078         self.analog_state_subscriber_ = rospy.Subscriber("/device_" + PRODUCT_CODE + "_" + device_SN +
00079                                                          "_analog_inputs_state", UInt16MultiArray,
00080                                                          self.analog_state_callback)
00081         self.digital_state_subscriber_ = rospy.Subscriber("/device_" + PRODUCT_CODE + "_" + device_SN +
00082                                                           "_digital_inputs_state", BoolArray,
00083                                                           self.digital_state_callback)
00084 
00085         self.last_analog_state = None
00086         self.last_digital_state = None
00087         self.last_period_start_time = [0.0, 0.0, 0.0, 0.0]
00088         self.average_period = [0.0, 0.0, 0.0, 0.0]
00089 
00090         # Initialize the PWM outputs to 0 to avoid interference with the digital output testing
00091         command_msg = UInt16MultiArray(None, [0, 0, 0, 0, 0, 0, 0, 0])
00092         self.PWM_command_publisher.publish(command_msg)
00093         rospy.sleep(0.2)
00094 
00095     def analog_state_callback(self, msg):
00096         with self.a_state_lock:
00097             self.last_analog_state = msg
00098 
00099     def digital_state_callback(self, msg):
00100         with self.d_state_lock:
00101             if self.PWM_testing:
00102                 for i, value in enumerate(self.last_digital_state.data):
00103                     # detect the falling edge of the PWM signal
00104                     if value and not msg.data[i]:
00105                         if self.last_period_start_time[i] != 0.0:
00106                             self.average_period[i] = (1 - PERIOD_ALPHA) * self.average_period[i] + \
00107                                                      PERIOD_ALPHA * (rospy.get_time() - self.last_period_start_time[i])
00108                         self.last_period_start_time[i] = rospy.get_time()
00109             self.last_digital_state = msg
00110 
00111     def check_digital_inputs(self, output_values):
00112         rospy.sleep(0.1)
00113         with self.d_state_lock:
00114             if self.last_digital_state is not None:
00115                 for i, value in enumerate(output_values):
00116                     if i & 1:
00117                         if self.last_digital_state.data[i/2] != value:
00118                             rospy.logerr("Wrong value in digital input " + str(i/2))
00119                             self.success = False
00120             else:
00121                 rospy.logerr("No digital input data recived from: " + self.device_SN)
00122                 self.success = False
00123 
00124     def test_digital_io_case(self, output_values):
00125         command_msg = BoolArray(output_values)
00126         self.digital_command_publisher.publish(command_msg)
00127         self.check_digital_inputs(output_values)
00128         rospy.sleep(DIGITAL_IO_WAIT)
00129 
00130     def test_digital_ios(self):
00131         rospy.loginfo("Testing digital I/O")
00132         self.test_digital_io_case([False, False, False, False, False, False, False, False])
00133         self.test_digital_io_case([False, True, False, False, False, False, False, False])
00134         self.test_digital_io_case([False, False, False, True, False, False, False, False])
00135         self.test_digital_io_case([False, False, False, False, False, True, False, False])
00136         self.test_digital_io_case([False, False, False, False, False, False, False, True])
00137         self.test_digital_io_case([False, True, False, True, False, True, False, True])
00138         self.test_digital_io_case([False, False, False, False, False, False, False, False])
00139         rospy.loginfo("Digital I/O test ended")
00140 
00141     def check_analog_inputs(self, output_values):
00142         rospy.sleep(0.1)
00143         with self.a_state_lock:
00144             if self.last_analog_state is not None:
00145                 for i, value in enumerate(output_values):
00146                     index = i
00147                     if (float(value) / float(self.last_analog_state.data[index])) > ANALOG_RATIO_UPPER or \
00148                        (float(value) / float(self.last_analog_state.data[index])) < ANALOG_RATIO_LOWER:
00149                         rospy.logerr("Wrong value in analog input " + str(index) + " set: " + str(value) +
00150                                      " measured: " + str(self.last_analog_state.data[index]) + " ratio: " +
00151                                      str(float(value) / float(self.last_analog_state.data[index])))
00152                         self.success = False
00153                     index = i + 2
00154                     if (float(value) / float(self.last_analog_state.data[index])) > ANALOG_RATIO_UPPER or \
00155                        (float(value) / float(self.last_analog_state.data[index])) < ANALOG_RATIO_LOWER:
00156                         rospy.logerr("Wrong value in analog input " + str(index) + " set: " + str(value) +
00157                                      " measured: " + str(self.last_analog_state.data[index]) + " ratio: " +
00158                                      str(float(value) / float(self.last_analog_state.data[index])))
00159                         self.success = False
00160             else:
00161                 rospy.logerr("No analog input data received from: " + self.device_SN)
00162                 self.success = False
00163 
00164     def test_analog_io_case(self, output_values):
00165         command_msg = UInt16MultiArray(None, output_values)
00166         self.analog_command_publisher.publish(command_msg)
00167         self.check_analog_inputs(output_values)
00168         rospy.sleep(ANALOG_IO_WAIT)
00169 
00170     def test_analog_ios(self):
00171         rospy.loginfo("Testing analog I/O")
00172         rospy.sleep(0.1)
00173         self.test_analog_io_case([0x200, 0x200])
00174         self.test_analog_io_case([0x200, 0xFF00])
00175         self.test_analog_io_case([0xFF00, 0x200])
00176         self.test_analog_io_case([0x200, 0x200])
00177         self.test_analog_io_case([0x300, 0x300])
00178         self.test_analog_io_case([0x1000, 0x1000])
00179         self.test_analog_io_case([0x5000, 0x5000])
00180         self.test_analog_io_case([0x8000, 0x8000])
00181         self.test_analog_io_case([0xA000, 0xA000])
00182         self.test_analog_io_case([0xC000, 0xC000])
00183         self.test_analog_io_case([0xD000, 0xD000])
00184         self.test_analog_io_case([0xE000, 0xE000])
00185         self.test_analog_io_case([0xF000, 0xF000])
00186         self.test_analog_io_case([0xFF00, 0xFF00])
00187         rospy.loginfo("Analog I/O test ended")
00188 
00189     def test_PWM_o_case(self, output_values):
00190         command_msg = UInt16MultiArray(None, output_values)
00191         self.PWM_testing = True
00192         self.PWM_command_publisher.publish(command_msg)
00193         # self.check_digital_inputs(output_values)
00194         rospy.sleep(PWM_O_WAIT)
00195         for i, value in enumerate(self.average_period):
00196             if value > MAX_PWM_PERIOD or value < MIN_PWM_PERIOD:
00197                 rospy.logerr("Wrong period in PWM output " + str(i) + " measured period: " + str(value))
00198                 self.success = False
00199         self.PWM_testing = False
00200 
00201     def test_PWM_outputs(self):
00202         rospy.loginfo("Testing PWM outputs")
00203         self.test_PWM_o_case([0xFFFE, 0x8000, 0xFFFE, 0x8000, 0xFFFE, 0x8000, 0xFFFE, 0x8000])
00204         rospy.loginfo("PWM outputs test ended")
00205 
00206     def ramp(self):
00207         for i in range(0, 65535, 0x100):
00208             command_msg = UInt16MultiArray(None, [i, i])
00209             self.analog_command_publisher.publish(command_msg)
00210             rospy.sleep(0.03)
00211 
00212     def run_test(self):
00213         self.test_digital_ios()
00214         self.test_analog_ios()
00215         self.test_PWM_outputs()
00216         # self.ramp()
00217         if self.success:
00218             rospy.loginfo("NO ERRORS DETECTED. Product code: " + PRODUCT_CODE + " SN: " + self.device_SN)
00219         else:
00220             rospy.logerr("TEST FAILED. ERRORS DETECTED!!!!!!!!!!!!!!!!!!!!!!!!!! Product code: " + PRODUCT_CODE +
00221                          " SN: " + self.device_SN)
00222 
00223 
00224 def main(argv):
00225     try:
00226         opts, args = getopt.getopt(argv, "h")
00227     except getopt.GetoptError:
00228         print 'io_test.py <SN_device_1> ... <SN_device_N>'
00229         sys.exit(2)
00230     for opt, arg in opts:
00231         if opt == '-h':
00232             print 'io_test.py <SN_device_1> ... <SN_device_N>'
00233             sys.exit()
00234 
00235     if len(args) == 0:
00236         print 'io_test.py <SN_device_1> ... <SN_device_N>'
00237         sys.exit(2)
00238 
00239     # init the ros node
00240     rospy.init_node('IO_test', anonymous=True)
00241     rospy.sleep(0.5)
00242 
00243     for arg in args:
00244         io_test = IoTest(arg)
00245         io_test.run_test()
00246 
00247     rospy.loginfo("I/O test ended")
00248     # subscribe until interrupted
00249     # rospy.spin()
00250 
00251 
00252 if __name__ == '__main__':
00253     main(sys.argv[1:])


sr_ronex_drivers
Author(s): Ugo Cupcic, Toni Oliver, Mark Pitchless
autogenerated on Thu Jun 6 2019 21:22:00