def _wait_for( serial_numbers, timeout = 5 ): """ Wait until the given serial numbers are all online :param serial_numbers: A collection of serial-numbers to wait for :param timeout: Number of seconds of maximum wait time :return: True if all have come online; False if timeout was reached """ did_some_waiting = False while True: # have_all_devices = True enabled_sns = enabled() for sn in serial_numbers: if sn not in enabled_sns: have_all_devices = False break # if have_all_devices: if did_some_waiting: # Wait an extra second, just in case -- let the devices properly power up log.d( 'all devices powered up' ) time.sleep( 1 ) return True # if timeout <= 0: return False timeout -= 1 time.sleep( 1 ) did_some_waiting = True
def query( monitor_changes = True ): """ Start a new LRS context, and collect all devices :param monitor_changes: If True, devices will update dynamically as they are removed/added """ global rs if not rs: return # # Before we can start a context and query devices, we need to enable all the ports # on the acroname, if any: if acroname: acroname.connect() # MAY THROW! acroname.enable_ports( sleep_on_change = 5 ) # make sure all connected! # # Get all devices, and store by serial-number global _device_by_sn, _context, _port_to_sn _context = rs.context() _device_by_sn = dict() try: log.d( 'discovering devices ...' ) log.debug_indent() for dev in _context.query_devices(): if dev.is_update_device(): sn = dev.get_info( rs.camera_info.firmware_update_id ) else: sn = dev.get_info( rs.camera_info.serial_number ) _device_by_sn[sn] = Device( sn, dev ) log.d( '...', dev ) finally: log.debug_unindent() # if monitor_changes: _context.set_devices_changed_callback( _device_change_callback )
def generate_cmake(builddir, testdir, testname, filelist): makefile = builddir + '/' + testdir + '/CMakeLists.txt' log.d(' creating:', makefile) handle = open(makefile, 'w') filelist = '\n '.join(filelist) handle.write(''' # This file is automatically generated!! # Do not modify or your changes will be lost! cmake_minimum_required( VERSION 3.1.0 ) project( ''' + testname + ''' ) set( SRC_FILES ''' + filelist + ''' ) add_executable( ''' + testname + ''' ${SRC_FILES} ) source_group( "Common Files" FILES ${ELPP_FILES} ${CATCH_FILES} ) set_property(TARGET ''' + testname + ''' PROPERTY CXX_STANDARD 11) target_link_libraries( ''' + testname + ''' ${DEPENDENCIES}) set_target_properties( ''' + testname + ''' PROPERTIES FOLDER "Unit-Tests/''' + os.path.dirname(testdir) + '''" ) target_include_directories(''' + testname + ''' PRIVATE ''' + src + ''') ''') handle.close()
def debug_dump(self): if self._priority != 1000: log.d( 'priority:', self._priority ) if self._timeout != 200: log.d( 'timeout:', self._timeout) if self._tags: log.d( 'tags:', self._tags ) if self._flags: log.d( 'flags:', self._flags ) if len(self._configurations) > 1: log.d( len( self._configurations ), 'configurations' )
def find_first_device_or_exit(): """ :return: the first device that was found, if no device is found the test is skipped. That way we can still run the unit-tests when no device is connected and not fail the tests that check a connected device """ import pyrealsense2 as rs c = rs.context() if not c.devices.size(): # if no device is connected we skip the test log.f("No device found") dev = c.devices[0] log.d( 'found', dev ) return dev
def debug_dump(self): if self._priority != 1000: log.d('priority:', self._priority) if self._timeout != 200: log.d('timeout:', self._timeout) if len(self._tags) > 1: log.d('tags:', {tag for tag in self._tags if tag != "exe" and tag != "py"}) if self._flags: log.d('flags:', self._flags) if len(self._configurations) > 1: log.d(len(self._configurations), 'configurations')
def expect(depth_frame=None, color_frame=None, nothing_else=False): """ Looks at the syncer queue and gets the next frame from it if available, checking its contents against the expected frame numbers. """ global syncer, playback_status f = syncer.poll_for_frame() if playback_status is not None: countdown = 50 # 5 seconds while not f and playback_status != rs.playback_status.stopped: countdown -= 1 if countdown == 0: break time.sleep(0.1) f = syncer.poll_for_frame() # NOTE: f will never be None if not f: test.check(depth_frame is None, "expected a depth frame") test.check(color_frame is None, "expected a color frame") return False log.d("Got", f) fs = rs.composite_frame(f) if fs: depth = fs.get_depth_frame() else: depth = rs.depth_frame(f) test.info("actual depth", depth) test.check_equal(depth_frame is None, not depth) if depth_frame is not None and depth: test.check_equal(depth.get_frame_number(), depth_frame) if fs: color = fs.get_color_frame() elif not depth: color = rs.video_frame(f) else: color = None test.info("actual color", color) test.check_equal(color_frame is None, not color) if color_frame is not None and color: test.check_equal(color.get_frame_number(), color_frame) if nothing_else: f = syncer.poll_for_frame() test.info("Expected nothing else; actual", f) test.check(not f) return True
def find_devices_by_product_line_or_exit( product_line ): """ :param product_line: The product line of the wanted devices :return: A list of devices of specific product line that was found, if no device is found the test is skipped. That way we can still run the unit-tests when no device is connected and not fail the tests that check a connected device """ import pyrealsense2 as rs c = rs.context() devices_list = c.query_devices(product_line) if devices_list.size() == 0: log.f( "No device of the", product_line, "product line was found" ) log.d( 'found', devices_list.size(), product_line, 'devices:', [dev for dev in devices_list] ) return devices_list
def load_specs_from_file( filename ): """ Loads a set of specs from a file: - Comments (#) are removed - Each word in the file is a spec :param filename: the path to the text file we want to load :return: a set of specs that can then be expanded to a set of serial-numbers (see expand_specs()) """ from rspy import file exceptions = set() for line, comment in file.split_comments( filename ): specs = line.split() if specs: log.d( '...', specs, comment and (' # ' + comment) or '', ) exceptions.update( specs ) return exceptions
def __init__( self, testname, exe = None, context = None ): """ :param testname: name of the test :param exe: full path to executable :param context: context in which the test will run """ global unit_tests_dir if exe and not os.path.isfile( exe ): log.d( "Tried to create exe test with invalid exe file: " + exe ) Test.__init__( self, testname ) self.exe = exe relative_test_path = self.find_source_path() if relative_test_path: self._config = TestConfigFromCpp( unit_tests_dir + os.sep + relative_test_path, context ) else: self._config = TestConfig(context)
def find_build_dir( dir ): """ Given a directory we know must be within the build tree, go up the tree until we find a file we know must be in the root build directory... :return: the build directory if found, or None otherwise """ build_dir = dir while True: if os.path.isfile( os.path.join( build_dir, 'CMakeCache.txt' )): log.d( 'assuming build dir path:', build_dir ) return build_dir base = os.path.dirname( build_dir ) if base == build_dir: log.d( 'could not find CMakeCache.txt; cannot assume build dir from', dir ) break build_dir = base
def run(cmd, stdout=None, timeout=200, append=False): """ Wrapper function for subprocess.run. If the child process times out or ends with a non-zero exit status an exception is raised! :param cmd: the command and argument for the child process, as a list :param stdout: path of file to direct the output of the process to (None to disable) :param timeout: number of seconds to give the process before forcefully ending it (None to disable) :param append: if True and stdout is not None, the log of the test will be appended to the file instead of overwriting it :return: the output written by the child, if stdout is None -- otherwise N/A """ log.d('running:', cmd) handle = None start_time = time.time() try: log.debug_indent() if stdout and stdout != subprocess.PIPE: if append: handle = open(stdout, "a") handle.write( "\n---------------------------------------------------------------------------------\n\n" ) handle.flush() else: handle = open(stdout, "w") stdout = handle rv = subprocess.run(cmd, stdout=stdout, stderr=subprocess.STDOUT, universal_newlines=True, timeout=timeout, check=True) result = rv.stdout if not result: result = [] else: result = result.split('\n') return result finally: if handle: handle.close() log.debug_unindent() run_time = time.time() - start_time log.d("test took", run_time, "seconds")
def discover(): """ Return all Acroname module specs in a list. Raise NoneFoundError if one is not found! """ log.d('discovering Acroname modules ...') # see https://acroname.com/reference/_modules/brainstem/module.html#Module.discoverAndConnect try: log.debug_indent() specs = brainstem.discover.findAllModules(brainstem.link.Spec.USB) if not specs: raise NoneFoundError() for spec in specs: log.d('...', spec) finally: log.debug_unindent() return specs
def generate_color_frame(frame_number, timestamp): """ """ global playback_status if playback_status is not None: raise RuntimeError("cannot generate frames when playing back") # global color_profile, domain, pixels, color_sensor, w, bpp color_frame = rs.software_video_frame() color_frame.pixels = pixels color_frame.stride = w * bpp color_frame.bpp = bpp color_frame.frame_number = frame_number color_frame.timestamp = timestamp color_frame.domain = domain color_frame.profile = color_profile # log.d("-->", color_frame) color_sensor.on_video_frame(color_frame)
def generate_depth_frame(frame_number, timestamp): """ """ global playback_status if playback_status is not None: raise RuntimeError("cannot generate frames when playing back") # global depth_profile, domain, pixels, depth_sensor, w, bpp depth_frame = rs.software_video_frame() depth_frame.pixels = pixels depth_frame.stride = w * bpp depth_frame.bpp = bpp depth_frame.frame_number = frame_number depth_frame.timestamp = timestamp depth_frame.domain = domain depth_frame.profile = depth_profile # log.d("-->", depth_frame) depth_sensor.on_video_frame(depth_frame)
def expand_specs(specs): """ Given a collection of configuration specs, expand them into actual serial numbers. Specs can be loaded from a file: see load_specs_from_file() :param specs: a collection of specs :return: a set of serial-numbers """ expanded = set() for spec in specs: sns = {sn for sn in _get_sns_from_spec(spec)} if sns: expanded.update(sns) else: # maybe the spec is a specific serial-number? if get(spec): expanded.add(spec) else: log.d('unknown spec:', spec) return expanded
def _device_change_callback(info): """ Called when librealsense detects a device change (see query()) """ global _device_by_sn new_device_by_sn = dict() for sn, dev in _device_by_sn.items(): if info.was_removed(dev): log.d('device removed:', sn) else: new_device_by_sn[sn] = dev for dev in info.get_new_devices(): if dev.is_update_device(): sn = dev.get_info(rs.camera_info.firmware_update_id) else: sn = dev.get_info(rs.camera_info.serial_number) log.d('device added:', dev) new_device_by_sn[sn] = dev _device_by_sn = new_device_by_sn
def __init__( self, sn, dev ): self._sn = sn self._dev = dev self._name = None if dev.supports( rs.camera_info.name ): self._name = dev.get_info( rs.camera_info.name ) self._product_line = None if dev.supports( rs.camera_info.product_line ): self._product_line = dev.get_info( rs.camera_info.product_line ) self._physical_port = dev.supports( rs.camera_info.physical_port ) and dev.get_info( rs.camera_info.physical_port ) or None self._usb_location = _get_usb_location( self._physical_port ) self._port = None if acroname: try: self._port = _get_port_by_loc( self._usb_location ) except Exception as e: log.e( 'Failed to get device port:', e ) log.d( ' physical port is', self._physical_port ) log.d( ' USB location is', self._usb_location ) self._removed = False
def connect( spec = None ): """ Connect to the hub. Raises RuntimeError on failure """ global hub if not hub: hub = brainstem.stem.USBHub3p() if spec: specs = [spec] else: specs = discover() spec = specs[0] result = hub.connectFromSpec( spec ) if result != brainstem.result.Result.NO_ERROR: raise RuntimeError( "failed to connect to acroname (result={})".format( result )) elif len(specs) > 1: log.d( 'connected to', spec )
def log_settings_differences( data ): global depth_sensor, sd depth_sensor.set_option(rs.option.visual_preset, int(rs.l500_visual_preset.low_ambient_light)) actual_data = str( sd.serialize_json() ) data_dict = json_to_dict( data ) actual_data_dict = json_to_dict( actual_data ) log.debug_indent() try: # logging the differences in the settings between the expected and the actual values for key in actual_data_dict.keys(): if key not in data_dict: log.d( "New setting added to json:", key) elif "Visual Preset" in key or "Temperature" in key or "temperature" in key: # the line regarding the visual preset will always be different because we load 1 from data but set it to # 3 for low ambient. Also all lines regarding temperatures depend on the camera and don't affect the preset continue elif data_dict[ key ] != actual_data_dict[ key ]: log.d( key, "was expected to have value of", data_dict[ key ], "but actually had value of", actual_data_dict[ key ]) finally: log.debug_unindent()
def enable_only( serial_numbers, recycle = False, timeout = 5 ): """ Enable only the devices corresponding to the given serial-numbers. This can work either with or without Acroname: without, the devices will simply be HW-reset, but other devices will still be present. NOTE: will raise an exception if any SN is unknown! :param serial_numbers: A collection of serial-numbers to enable - all others' ports are disabled and will no longer be usable! :param recycle: If False, the devices will not be reset if they were already enabled. If True, the devices will be recycled by disabling the port, waiting, then re-enabling :param timeout: The maximum seconds to wait to make sure the devices are indeed online """ if acroname: # ports = [ get_port( sn ) for sn in serial_numbers ] # if recycle: # log.d( 'recycling ports via acroname:', ports ) # acroname.disable_ports( acroname.ports() ) _wait_until_removed( serial_numbers, timeout = timeout ) # acroname.enable_ports( ports ) # else: # acroname.enable_ports( ports, disable_other_ports = True ) # _wait_for( serial_numbers, timeout = timeout ) # elif recycle: # hw_reset( serial_numbers ) # else: log.d( 'no acroname; ports left as-is' )
def set_env_vars( env_vars ): """ We want certain environment variables set when we get here. We assume they're not set. However, it is impossible to change the current running environment to see them. Instead, we rerun ourselves in a child process that inherits the environment we set. To do this, we depend on a specific argument in sys.argv that tells us this is the rerun (meaning child process). When we see it, we assume the variables are set and don't do anything else. For this to work well, the environment variable requirement (set_env_vars call) should appear as one of the first lines of the test. :param env_vars: A dictionary where the keys are the name of the environment variable and the values are the wanted values in string form (environment variables must be strings) """ if sys.argv[-1] != 'rerun': log.d( 'environment variables needed:', env_vars ) for env_var, val in env_vars.items(): os.environ[env_var] = val cmd = [sys.executable] if 'site' not in sys.modules: # -S : don't imply 'import site' on initialization cmd += ["-S"] if sys.flags.verbose: # -v : verbose (trace import statements) cmd += ["-v"] cmd += sys.argv # --debug, or any other args cmd += ["rerun"] log.d( 'running:', cmd ) p = subprocess.run( cmd, stderr=subprocess.PIPE, universal_newlines=True ) sys.exit( p.returncode ) log.d( 'rerun detected' ) sys.argv = sys.argv[:-1] # Remove the rerun
def find_includes(filepath, filelist=set()): """ Recursively searches a .cpp file for #include directives and returns a set of all of them. :return: a list of all includes found """ filedir = os.path.dirname(filepath) try: log.debug_indent() for include_line in file.grep(r'^\s*#\s*include\s+("(.*)"|<(.*)>)\s*$', filepath): m = include_line['match'] index = include_line['index'] include = find_include( m.group(2), filedir) or find_include_in_dirs( m.group(2)) or find_include_in_dirs(m.group(3)) if include: if include in filelist: log.d(m.group(0), '->', include, '(already processed)') else: log.d(m.group(0), '->', include) filelist.add(include) filelist = find_includes(include, filelist) else: log.d('not found:', m.group(0)) finally: log.debug_unindent() return filelist
def _wait_for(serial_numbers, timeout=5): """ Wait until the given serial numbers are all online :param serial_numbers: A collection of serial-numbers to wait for :param timeout: Number of seconds of maximum wait time :return: True if all have come online; False if timeout was reached """ did_some_waiting = False # # In Linux, we don't have an active notification mechanism - we query devices every 5 seconds # (see POLLING_DEVICES_INTERVAL_MS) - so we add extra timeout if timeout and platform.system() == 'Linux': timeout += 5 # while True: # have_all_devices = True enabled_sns = enabled() for sn in serial_numbers: if sn not in enabled_sns: have_all_devices = False break # if have_all_devices: if did_some_waiting: # Wait an extra second, just in case -- let the devices properly power up #log.d( 'all devices powered up' ) time.sleep(1) return True # if timeout <= 0: if did_some_waiting: log.d('timed out') return False timeout -= 1 time.sleep(1) did_some_waiting = True
def subprocess_run(cmd, stdout = None): log.d( 'running:', cmd ) handle = None try: log.debug_indent() if stdout and stdout != subprocess.PIPE: handle = open( stdout, "w" ) stdout = handle rv = subprocess.run( cmd, stdout = stdout, stderr = subprocess.STDOUT, universal_newlines = True, check = True) result = rv.stdout if not result: result = [] else: result = result.split( '\n' ) return result finally: if handle: handle.close() log.debug_unindent()
def query(monitor_changes=True): """ Start a new LRS context, and collect all devices :param monitor_changes: If True, devices will update dynamically as they are removed/added """ global rs if not rs: return # # Before we can start a context and query devices, we need to enable all the ports # on the acroname, if any: if acroname: if not acroname.hub: acroname.connect() # MAY THROW! acroname.enable_ports( sleep_on_change=5) # make sure all connected! # # Get all devices, and store by serial-number global _device_by_sn, _context, _port_to_sn _context = rs.context() _device_by_sn = dict() try: log.d('discovering devices ...') log.debug_indent() for retry in range(3): try: devices = _context.query_devices() break except RuntimeError as e: log.d('FAILED to query devices:', e) if retry > 1: log.e('FAILED to query devices', retry + 1, 'times!') raise else: time.sleep(1) for dev in devices: # The FW update ID is always available, it seems, and is the ASIC serial number # whereas the Serial Number is the OPTIC serial number and is only available in # non-recovery devices. So we use the former... sn = dev.get_info(rs.camera_info.firmware_update_id) device = Device(sn, dev) _device_by_sn[sn] = device log.d( '... port {}:'.format(device.port is None and '?' or device.port), sn, dev) finally: log.debug_unindent() # if monitor_changes: _context.set_devices_changed_callback(_device_change_callback)
def test_option_changes(sensor): options = sensor.get_supported_options() for option in options: try: if sensor.is_option_read_only(option): continue old_value = sensor.get_option(option) range = sensor.get_option_range(option) new_value = range.min if old_value == new_value: new_value = range.max if not log.d(str(option), old_value, '->', new_value): test.info(str(option), new_value, persistent=True) set_new_value(sensor, option, new_value) sensor.set_option(option, old_value) except: test.unexpected_exception() break finally: test.reset_info(persistent=True)
def _device_change_callback( info ): """ Called when librealsense detects a device change (see query()) """ global _device_by_sn for device in _device_by_sn.values(): if device.enabled and info.was_removed( device.handle ): log.d( 'device removed:', device.serial_number ) device._removed = True for handle in info.get_new_devices(): if handle.is_update_device(): sn = handle.get_info( rs.camera_info.firmware_update_id ) else: sn = handle.get_info( rs.camera_info.serial_number ) log.d( 'device added:', handle ) if sn in _device_by_sn: _device_by_sn[sn]._removed = False else: log.d( 'New device detected!?' ) # shouldn't see new devices... _device_by_sn[sn] = Device( sn, handle )
depth_options = depth_sensor.get_supported_options() color_options = color_sensor.get_supported_options() test.start("Checking for frame drops when setting any option") for option in depth_options: try: if depth_sensor.is_option_read_only(option): continue old_value = depth_sensor.get_option(option) range = depth_sensor.get_option_range(option) new_value = range.min if old_value == new_value: new_value = range.max if not log.d(str(option), old_value, '->', new_value): test.info(str(option), new_value, persistent=True) set_new_value(depth_sensor, option, new_value) depth_sensor.set_option(option, old_value) except: test.unexpected_exception() test.abort() finally: test.reset_info(persistent=True) for option in color_options: try: if color_sensor.is_option_read_only(option): continue new_value = color_sensor.get_option_range(option).min set_new_value(color_sensor, option, new_value)
# find the update tool exe fw_updater_exe = None for tool in file.find(repo.build, '(^|/)rs-fw-update.exe$'): fw_updater_exe = os.path.join(repo.build, tool) if not fw_updater_exe: log.f("Could not find the update tool file (rs-fw-update.exe)") devices.query(monitor_changes=False) sn_list = devices.all() # acroname should ensure there is always 1 available device if len(sn_list) != 1: log.f("Expected 1 device, got", len(sn_list)) device = devices.get_first(sn_list).handle log.d('found:', device) product_line = device.get_info(rs.camera_info.product_line) log.d('product line:', product_line) ############################################################################### # test.start("Update FW") # check if recovery. If so recover recovered = False if device.is_update_device(): log.d("recovering device ...") try: # TODO: this needs to improve for L535 image_name = product_line[:-2] + "XX_FW_Image-" image_mask = '(^|/)' + image_name + '(\d+\.){4}bin$' image_file = None