Beispiel #1
0
def main_launcher():
    options = parse_arguments()
    # flip the default vs the individual launchers - wait for all of the backends
    options.wait = True

    # save the total state
    _opts = get_printable_options_dict(options,
                                       include_experiment_and_playlist=True)
    with open(options.record_file.replace('.h5', '.config.yml'), 'wt') as f:
        yaml.safe_dump(_opts, f)

    log = logging.getLogger('flyvr.main')

    flyvr_shared_state = SharedState(options=options,
                                     logger=None,
                                     where='main')

    # start the IPC bus first as it is needed by many subsystems
    ipc_bus = ConcurrentTask(task=run_main_relay, comms=None, taskinitargs=[])
    ipc_bus.start()

    # start the GUI
    gui = ConcurrentTask(task=run_main_state_gui,
                         comms=None,
                         taskinitargs=[None, True])
    gui.start()

    backend_wait = [BACKEND_FICTRAC]

    trac_drv = _get_fictrac_driver(options, log)
    if trac_drv is not None:
        fictrac_task = ConcurrentTask(task=trac_drv.run,
                                      comms=None,
                                      taskinitargs=[options])
        fictrac_task.start()

        # wait till fictrac is processing frames
        flyvr_shared_state.wait_for_backends(BACKEND_FICTRAC)
        log.info('fictrac is ready')

    # start the other mainloops

    # these always run
    hwio = ConcurrentTask(task=run_phidget_io,
                          comms=None,
                          taskinitargs=[options])
    backend_wait.append(BACKEND_HWIO)
    hwio.start()

    daq = ConcurrentTask(task=run_io, comms=None, taskinitargs=[options])
    backend_wait.append(BACKEND_DAQ)
    daq.start()

    # these are optional
    if options.keepalive_video or options.playlist.get('video'):
        video = ConcurrentTask(task=run_video_server,
                               comms=None,
                               taskinitargs=[options])
        backend_wait.append(BACKEND_VIDEO)
        video.start()
    else:
        video = None
        log.info(
            'not starting video backend (playlist empty or keepalive_video not specified)'
        )

    if options.keepalive_audio or options.playlist.get('audio'):
        audio = ConcurrentTask(task=run_sound_server,
                               comms=None,
                               taskinitargs=[options])
        backend_wait.append(BACKEND_AUDIO)
        audio.start()
    else:
        audio = None
        log.info(
            'not starting video backend (playlist empty or keepalive_video not specified)'
        )

    log.info('waiting %ss for %r to be ready' % (60, backend_wait))
    if flyvr_shared_state.wait_for_backends(*backend_wait, timeout=60):

        if options.delay < 0:
            log.info('waiting for manual start signal')
            flyvr_shared_state.wait_for_start()
        elif options.delay >= 0:
            if options.delay > 0:
                log.info('delaying startup %ss' % options.delay)
                time.sleep(options.delay)
            log.info('sending start signal')
            flyvr_shared_state.signal_start()

        for i in itertools.count():
            try:
                inputimeout(
                    '\n---------------\nPress any key to finish\n---------------\n\n'
                    if i == 0 else '', 1)
                flyvr_shared_state.signal_stop().join(timeout=5)
                break
            except TimeoutOccurred:
                if flyvr_shared_state.is_stopped():
                    # stopped from elsewhere (gui)
                    break

    else:
        log.error(
            'not all required backends became ready - please check logs for '
            'error messages')
        flyvr_shared_state.signal_stop()

    log.info('stopped')

    for task in (ipc_bus, gui, hwio, daq, video, audio):
        if task is not None:
            log.debug('closing subprocess: %r' % task)
            task.close()

    log.info('finished')
Beispiel #2
0
    def run(self, options=None):
        """
        Start the the FicTrac process and block till it closes. This function will poll a shared memory region for
        changes in tracking data and invoke a control function when they occur. FicTrac is assumed to exist on the
        system path.

        Args:
            options: options loaded from FlyVR config file. If None, driver runs without logging enabled, this is useful
                for testing.

        :return:
        """

        if options is not None:
            setup_logging(options)

            setup_experiment(options)
            if options.experiment:
                self._log.info('initialized experiment %r' %
                               options.experiment)
            self.experiment = options.experiment

            # fixme: this should be threaded and context manager to close
            log_server = DatasetLogServer()

            flyvr_shared_state = SharedState(
                options=options,
                logger=log_server.start_logging_server(options.record_file),
                where=BACKEND_FICTRAC)
            if self.experiment:
                # noinspection PyProtectedMember
                self.experiment._set_shared_state(flyvr_shared_state)

            # Setup dataset to log FicTrac data to.
            flyvr_shared_state.logger.create(
                "/fictrac/output",
                shape=[2048, NUM_FICTRAC_FIELDS],
                maxshape=[None, NUM_FICTRAC_FIELDS],
                dtype=np.float64,
                chunks=(2048, NUM_FICTRAC_FIELDS))
            flyvr_shared_state.logger.log("/fictrac/output",
                                          H5_DATA_VERSION,
                                          attribute_name='__version')
        else:
            flyvr_shared_state = None

        self.fictrac_signals = new_mmap_signals_buffer()

        # Start FicTrac
        with open(self.console_output_file, "wb") as out:

            self.fictrac_process = subprocess.Popen(
                [self.fictrac_bin_fullpath, self.config_file],
                stdout=out,
                stderr=subprocess.STDOUT)

            # Setup the shared memory conneciton and peek at the frame counter
            data = new_mmap_shmem_buffer()
            first_frame_count = data.frame_cnt
            old_frame_count = data.frame_cnt

            running = True
            self._log.info("waiting for fictrac updates in shared memory")

            semaphore = self._open_fictrac_semaphore()

            # Process FicTrac updates in shared memory
            while (self.fictrac_process.poll() is
                   None) and running and semaphore:

                # Acquire the semaphore copy the current fictrac state.
                try:
                    semaphore.acquire(timeout_ms=1000)
                    data_copy = SHMEMFicTracState()
                    ctypes.pointer(data_copy)[0] = data
                    semaphore.release()
                except FileNotFoundError:
                    # Semaphore is gone, lets shutdown things.
                    break
                except OSError:
                    break

                new_frame_count = data_copy.frame_cnt

                if old_frame_count != new_frame_count:
                    # If this is our first frame incremented, then send a signal to the
                    # that we have started processing frames
                    if old_frame_count == first_frame_count:
                        if flyvr_shared_state:
                            _ = flyvr_shared_state.signal_ready(
                                BACKEND_FICTRAC)

                    if new_frame_count - old_frame_count != 1:
                        # self.fictrac_process.terminate()
                        self._log.error(
                            "frame counter jumped by more than 1 (%s vs %s)" %
                            (old_frame_count, new_frame_count))

                    old_frame_count = new_frame_count

                    # Log the FicTrac data to our master log file.
                    if flyvr_shared_state:
                        flyvr_shared_state.logger.log(
                            '/fictrac/output', fictrac_state_to_vec(data_copy))

                    if self.experiment is not None:
                        self.experiment.process_state(data_copy)

                # If we detect it is time to shutdown, kill the FicTrac process
                if flyvr_shared_state and flyvr_shared_state.is_stopped():
                    running = False

            # Try to close up the semaphore
            try:
                if semaphore:
                    semaphore.close()
            except OSError:
                pass

        self.stop()  # blocks

        self._log.info('fictrac process finished')

        # Get the fictrac process return code
        if self.fictrac_process.returncode is not None and self.fictrac_process.returncode != 0:
            self._log.error(
                'fictrac failed because of an application error. Consult the fictrac console output file'
            )

            if flyvr_shared_state:
                flyvr_shared_state.runtime_error(2)
Beispiel #3
0
class FlyVRStateGui(QWidget):

    STATE = [
        'FICTRAC_FRAME_NUM', 'SOUND_OUTPUT_NUM_SAMPLES_WRITTEN',
        'VIDEO_OUTPUT_NUM_FRAMES', 'DAQ_OUTPUT_NUM_SAMPLES_WRITTEN',
        'DAQ_INPUT_NUM_SAMPLES_READ'
    ]
    FPS = 30

    def __init__(self, app, quit_app_on_stop):
        # noinspection PyArgumentList
        super().__init__(parent=None)
        self._app = app
        self._quit_app_on_stop = quit_app_on_stop

        self._entries = {}

        self._tick = 0
        self._fn0 = self._t0 = 0

        self._lbl_backends = None
        self._lbl_started = None
        self._lbl_fps = None

        self._init_ui()

        self.flyvr_shared_state = SharedState(options=None,
                                              logger=None,
                                              where='gui')

        self._timer = QTimer()
        # noinspection PyUnresolvedReferences
        self._timer.timeout.connect(self._update_state)
        self._timer.start(1000 / FlyVRStateGui.FPS)

    def _update_state(self):
        for s in FlyVRStateGui.STATE:
            v = getattr(self.flyvr_shared_state, s)
            self._entries[s].setText(str(v or 0))

        self._lbl_backends.setText(', '.join(
            self.flyvr_shared_state.backends_ready))
        self._lbl_started.setText(str(self.flyvr_shared_state.is_started()))

        # every second-ish
        if (self._tick % self.FPS) == 0:
            fictrac_fps = (self.flyvr_shared_state.FICTRAC_FRAME_NUM -
                           self._fn0) / (time.time() - self._t0)
            self._lbl_fps.setText('%.1f' % fictrac_fps)

            self._fn0 = self.flyvr_shared_state.FICTRAC_FRAME_NUM
            self._t0 = time.time()

        self._tick += 1

        if self._quit_app_on_stop:
            if self.flyvr_shared_state.is_stopped():
                self._app.quit()

    def _clicked_clear(self):
        for s in FlyVRStateGui.STATE:
            self._entries[s].setText('')

    def _clicked_start(self):
        self.flyvr_shared_state.signal_start()

    def _clicked_stop(self):
        self.flyvr_shared_state.signal_stop().join(2)

    # noinspection PyUnresolvedReferences,PyArgumentList
    def _init_ui(self):
        self.setWindowTitle('FlyVR')

        layout = QGridLayout()

        # noinspection PyArgumentList
        def _build_label(_name, _row):
            _lbl = QLabel('&%s' % _name, self)
            _edt = QLineEdit(self)
            _edt.setReadOnly(True)
            _lbl.setBuddy(_edt)

            layout.addWidget(_lbl, _row, 0)
            layout.addWidget(_edt, _row, 1, 1, 2)

            return _edt

        row = 0
        for s in FlyVRStateGui.STATE:
            self._entries[s] = _build_label(s, row)
            row += 1

        self._lbl_fps = _build_label('FicTrac Framerate', row)
        row += 1

        self._lbl_backends = _build_label('Backends Ready', row)
        row += 1

        self._lbl_started = _build_label('Experiment Started', row)
        row += 1

        # clear = QPushButton('&Clear')
        # clear.clicked.connect(self._clicked_clear)
        # layout.addWidget(clear, row, 2)
        # row += 1

        start = QPushButton('&Start Experiment')
        start.clicked.connect(self._clicked_start)
        stop = QPushButton('&Stop FlyVR')
        stop.clicked.connect(self._clicked_stop)

        layout.addWidget(start, row, 1)
        layout.addWidget(stop, row, 2)

        row += 1

        self.setLayout(layout)