Note: this function will check the experiments directory for a
        special file, scheduler.info, that details how often each
        experiment should be run and the last time the experiment was
        run. If the time since the experiment was run is shorter than
        the scheduled interval in seconds, then the experiment will
        not be run.

        :param data_dir:
        :return:

def run(self, data_dir=None):

        """
        Note: this function will check the experiments directory for a
        special file, scheduler.info, that details how often each
        experiment should be run and the last time the experiment was
        run. If the time since the experiment was run is shorter than
        the scheduled interval in seconds, then the experiment will
        not be run.

        :param data_dir:
        :return:
        """
        # XXX: android build needs this. refactor
        if data_dir:
            centinel_home = data_dir
            self.config['dirs']['results_dir'] = os.path.join(centinel_home,
                                                              'results')

        logging.info('Centinel started.')

        if not os.path.exists(self.config['dirs']['results_dir']):
            logging.warn("Creating results directory in "
                         "%s" % (self.config['dirs']['results_dir']))
            os.makedirs(self.config['dirs']['results_dir'])
        logging.debug("Results directory: %s" % (self.config['dirs']['results_dir']))

        # load scheduler information
        sched_filename = os.path.join(self.config['dirs']['experiments_dir'],
                                      'scheduler.info')

        logging.debug("Loading scheduler file.")
        sched_info = {}
        if os.path.exists(sched_filename):
            with open(sched_filename, 'r') as file_p:
                try:
                    sched_info = json.load(file_p)
                except Exception as exp:
                    logging.error("Failed to load the "
                                  "scheduler: %s" % str(exp))
                    return

        logging.debug("Scheduler file loaded.")

        logging.debug("Processing the experiment schedule.")
        for name in sched_info:

            # check if we should preempt on the experiment (if the
            # time to run next is greater than the current time) and
            # store the last run time as now
            #
            # Note: if the experiment is not in the scheduler, then it
            # will not be run at all.
            run_next = sched_info[name]['last_run']
            run_next += sched_info[name]['frequency']
            if run_next > time.time():
                run_next_str = datetime.fromtimestamp(long(run_next))
                logging.debug("Skipping %s, it will "
                              "be run on or after %s." % (name, run_next_str))
                continue

            # backward compatibility with older-style scheduler
            if 'python_exps' not in sched_info[name]:
                self.run_exp(name=name)
            else:
                exps = sched_info[name]['python_exps'].items()
                for python_exp, exp_config in exps:
                    logging.debug("Running %s." % python_exp)
                    self.run_exp(name=python_exp, exp_config=exp_config, schedule_name=name)
                    logging.debug("Finished running %s." % python_exp)
            sched_info[name]['last_run'] = time.time()

        logging.debug("Updating timeout values in scheduler.")
        # write out the updated last run times
        with open(sched_filename, 'w') as file_p:
            json.dump(sched_info, file_p, indent=2,
                      separators=(',', ': '))

        self.consolidate_results()

        logging.info("Finished running experiments. "
                     "Look in %s for results." % (self.config['dirs']['results_dir']))