示例#1
0
def run_task():
    data_logger = Data_logger(print_func=print)
    board = None
    while not board:
        i = input('Enter serial port of board or board number, or [s] to scan for pyboards: ')
        if i == 's':
            pyboard_serials = {j+1: b for (j,b) in 
                enumerate([c[0] for c in list_ports.comports() if 'Pyboard' in c[1]])}
            unknown_usb_serials = {j+len(pyboard_serials)+1: b for (j,b) in 
                enumerate([c[0] for c in list_ports.comports() if 'USB Serial Device' in c[1]])}
            if not (pyboard_serials or unknown_usb_serials):
                print('\nNo Pyboards found.\n' )
                continue
            else:
                if pyboard_serials:
                    print('\nPyboards found on the following serial ports:\n')
                    for b in pyboard_serials.keys():
                        print('{}: {}\n'.format(b, pyboard_serials[b]))
                if unknown_usb_serials:
                    print('\nPossible Pyboards found on the following serial ports:\n')
                    for b in unknown_usb_serials.keys():
                        print('{}: {}\n'.format(b, unknown_usb_serials[b]))
                pyboard_serials.update(unknown_usb_serials)
                while True:
                    k = input('Select Pyboard:')
                    try:
                        port = pyboard_serials[int(k)]
                        break
                    except (KeyError, ValueError):
                        print('\nInput not recognised, valid inputs: {}\n'.format(list(pyboard_serials.keys())))
        else:
            try: # Check if input is an integer corresponding to a setup number.
                port = config.board_serials[int(i)]
            except (KeyError, ValueError):
                port = i
        try:
            board = Pycboard(port, verbose=False, data_logger=data_logger)
        except SerialException:
            print('\nUnable to open serial connection {}, Check serial port is correct.\n'
                  'If port is correct, try resetting pyboard with reset button.\n'.format(port))

    print('\nSerial connection OK. Micropython version: {}'.format(board.micropython_version))

    if not board.status['framework']:
        board.load_framework()

    task_select_menu(board)
class Run_task_tab(QtGui.QWidget):
    def __init__(self, parent=None):
        super(QtGui.QWidget, self).__init__(parent)

        # Variables.
        self.GUI_main = self.parent()
        self.board = None  # Pycboard class instance.
        self.task = None  # Task currently uploaded on pyboard.
        self.task_hash = None  # Used to check if file has changed.
        self.data_dir = None
        self.connected = False  # Whether gui is conencted to pyboard.
        self.uploaded = False  # Whether selected task file is on board.
        self.fresh_task = None  # Whether task has been run or variables edited.
        self.running = False
        self.subject_changed = False
        self.variables_dialog = None

        # GUI groupbox.

        self.status_groupbox = QtGui.QGroupBox('Status')

        self.status_text = QtGui.QLineEdit('Not connected')
        self.status_text.setReadOnly(True)

        self.guigroup_layout = QtGui.QHBoxLayout()
        self.guigroup_layout.addWidget(self.status_text)
        self.status_groupbox.setLayout(self.guigroup_layout)

        # Board groupbox

        self.board_groupbox = QtGui.QGroupBox('Setup')

        self.board_label = QtGui.QLabel('Select:')
        self.board_select = QtGui.QComboBox()
        self.board_select.setEditable(True)
        self.board_select.setFixedWidth(100)
        self.connect_button = QtGui.QPushButton('Connect')
        self.config_button = QtGui.QPushButton('Config')

        self.boardgroup_layout = QtGui.QHBoxLayout()
        self.boardgroup_layout.addWidget(self.board_label)
        self.boardgroup_layout.addWidget(self.board_select)
        self.boardgroup_layout.addWidget(self.connect_button)
        self.boardgroup_layout.addWidget(self.config_button)
        self.board_groupbox.setLayout(self.boardgroup_layout)

        self.connect_button.clicked.connect(
            lambda: self.disconnect() if self.connected else self.connect())
        self.config_button.clicked.connect(self.open_config_dialog)

        # File groupbox

        self.file_groupbox = QtGui.QGroupBox('Data file')

        self.data_dir_label = QtGui.QLabel('Data dir:')
        self.data_dir_text = QtGui.QLineEdit(data_dir)
        self.data_dir_button = QtGui.QPushButton('...')
        self.data_dir_button.setFixedWidth(30)
        self.subject_label = QtGui.QLabel('Subject ID:')
        self.subject_text = QtGui.QLineEdit()
        self.subject_text.setFixedWidth(80)
        self.subject_text.setMaxLength(12)

        self.filegroup_layout = QtGui.QHBoxLayout()
        self.filegroup_layout.addWidget(self.data_dir_label)
        self.filegroup_layout.addWidget(self.data_dir_text)
        self.filegroup_layout.addWidget(self.data_dir_button)
        self.filegroup_layout.addWidget(self.subject_label)
        self.filegroup_layout.addWidget(self.subject_text)
        self.file_groupbox.setLayout(self.filegroup_layout)

        self.data_dir_text.textChanged.connect(self.test_data_path)
        self.data_dir_button.clicked.connect(self.select_data_dir)
        self.subject_text.textChanged.connect(self.test_data_path)

        # Task groupbox

        self.task_groupbox = QtGui.QGroupBox('Task')

        self.task_label = QtGui.QLabel('Task:')
        self.task_select = QtGui.QComboBox()
        self.upload_button = QtGui.QPushButton('Upload')
        self.variables_button = QtGui.QPushButton('Variables')

        self.taskgroup_layout = QtGui.QHBoxLayout()
        self.taskgroup_layout.addWidget(self.task_label)
        self.taskgroup_layout.addWidget(self.task_select)
        self.taskgroup_layout.addWidget(self.upload_button)
        self.taskgroup_layout.addWidget(self.variables_button)
        self.task_groupbox.setLayout(self.taskgroup_layout)

        self.task_select.currentTextChanged.connect(self.task_changed)
        self.upload_button.clicked.connect(self.setup_task)

        # Session groupbox.

        self.session_groupbox = QtGui.QGroupBox('Session')

        self.start_button = QtGui.QPushButton('Start')
        self.stop_button = QtGui.QPushButton('Stop')

        self.sessiongroup_layout = QtGui.QHBoxLayout()
        self.sessiongroup_layout.addWidget(self.start_button)
        self.sessiongroup_layout.addWidget(self.stop_button)
        self.session_groupbox.setLayout(self.sessiongroup_layout)

        self.start_button.clicked.connect(self.start_task)
        self.stop_button.clicked.connect(self.stop_task)

        # Log text and task plots.

        self.log_textbox = QtGui.QTextEdit()
        self.log_textbox.setFont(QtGui.QFont('Courier', 9))
        self.log_textbox.setReadOnly(True)

        self.task_plot = Task_plot()
        self.data_logger = Data_logger(print_func=self.print_to_log,
                                       data_consumers=[self.task_plot])

        # Main layout

        self.vertical_layout = QtGui.QVBoxLayout()
        self.horizontal_layout_1 = QtGui.QHBoxLayout()
        self.horizontal_layout_2 = QtGui.QHBoxLayout()
        self.horizontal_layout_3 = QtGui.QHBoxLayout()

        self.horizontal_layout_1.addWidget(self.status_groupbox)
        self.horizontal_layout_1.addWidget(self.board_groupbox)
        self.horizontal_layout_2.addWidget(self.file_groupbox)
        self.horizontal_layout_3.addWidget(self.task_groupbox)
        self.horizontal_layout_3.addWidget(self.session_groupbox)
        self.vertical_layout.addLayout(self.horizontal_layout_1)
        self.vertical_layout.addLayout(self.horizontal_layout_2)
        self.vertical_layout.addLayout(self.horizontal_layout_3)
        self.vertical_layout.addWidget(self.log_textbox, 20)
        self.vertical_layout.addWidget(self.task_plot, 80)
        self.setLayout(self.vertical_layout)

        # Create timers

        self.update_timer = QtCore.QTimer(
        )  # Timer to regularly call update() during run.
        self.update_timer.timeout.connect(self.update)

        # Initial setup.

        self.disconnect()  # Set initial state as disconnected.

    # General methods

    def print_to_log(self, print_string, end='\n'):
        self.log_textbox.moveCursor(QtGui.QTextCursor.End)
        self.log_textbox.insertPlainText(print_string + end)
        self.log_textbox.moveCursor(QtGui.QTextCursor.End)
        self.GUI_main.app.processEvents(
        )  # To update gui during long operations that print progress.

    def test_data_path(self):
        # Checks whether data dir and subject ID are valid.
        self.data_dir = self.data_dir_text.text()
        subject_ID = self.subject_text.text()
        if os.path.isdir(self.data_dir) and subject_ID:
            self.start_button.setText('Record')
            return True
        else:
            self.start_button.setText('Start')
            return False

    def refresh(self):
        # Called regularly when framework not running.
        if self.GUI_main.setups_tab.available_setups_changed:
            self.board_select.clear()
            self.board_select.addItems(self.GUI_main.setups_tab.setup_names)
        if self.GUI_main.available_tasks_changed:
            self.task_select.clear()
            self.task_select.addItems(sorted(self.GUI_main.available_tasks))
        if self.task:
            try:
                task_path = os.path.join(tasks_dir, self.task + '.py')
                if not self.task_hash == _djb2_file(
                        task_path):  # Task file modified.
                    self.task_changed()
            except FileNotFoundError:
                pass

    def open_config_dialog(self):
        '''Open the config dialog and update GUI as required by chosen config.'''
        self.GUI_main.config_dialog.exec_(self.board)
        self.task_changed()
        if self.GUI_main.config_dialog.disconnect:
            self.disconnect()

    # Widget methods.

    def connect(self):
        # Connect to pyboard.
        try:
            self.status_text.setText('Connecting...')
            self.board_select.setEnabled(False)
            self.variables_button.setEnabled(False)
            self.connect_button.setEnabled(False)
            self.repaint()
            port = self.GUI_main.setups_tab.get_port(
                self.board_select.currentText())
            self.board = Pycboard(port,
                                  print_func=self.print_to_log,
                                  data_logger=self.data_logger)
            self.connected = True
            self.config_button.setEnabled(True)
            self.task_groupbox.setEnabled(True)
            self.connect_button.setEnabled(True)
            self.connect_button.setText('Disconnect')
            self.status_text.setText('Connected')
        except SerialException:
            self.status_text.setText('Connection failed')
            self.print_to_log('Connection failed.')
            self.connect_button.setEnabled(True)
        if self.connected and not self.board.status['framework']:
            self.board.load_framework()

    def disconnect(self):
        # Disconnect from pyboard.
        if self.board: self.board.close()
        self.board = None
        self.task_groupbox.setEnabled(False)
        self.file_groupbox.setEnabled(False)
        self.session_groupbox.setEnabled(False)
        self.config_button.setEnabled(False)
        self.board_select.setEnabled(True)
        self.connect_button.setText('Connect')
        self.status_text.setText('Not connected')
        self.task_changed()
        self.connected = False

    def task_changed(self):
        self.uploaded = False
        self.upload_button.setText('Upload')
        self.start_button.setEnabled(False)

    def setup_task(self):
        try:
            task = self.task_select.currentText()
            if self.uploaded:
                self.status_text.setText('Resetting task..')
            else:
                self.status_text.setText('Uploading..')
                self.task_hash = _djb2_file(
                    os.path.join(tasks_dir, task + '.py'))
            self.start_button.setEnabled(False)
            self.variables_button.setEnabled(False)
            self.repaint()
            self.board.setup_state_machine(task, uploaded=self.uploaded)
            if self.variables_dialog:
                self.variables_button.clicked.disconnect()
                self.variables_dialog.deleteLater()
            self.variables_dialog = Variables_dialog(self, self.board)
            self.variables_button.clicked.connect(self.variables_dialog.exec_)
            self.variables_button.setEnabled(True)
            self.task_plot.set_state_machine(self.board.sm_info)
            self.file_groupbox.setEnabled(True)
            self.session_groupbox.setEnabled(True)
            self.start_button.setEnabled(True)
            self.stop_button.setEnabled(False)
            self.status_text.setText('Uploaded : ' + task)
            self.task = task
            self.fresh_task = True
            self.uploaded = True
            self.upload_button.setText('Reset')
        except PyboardError:
            self.status_text.setText('Error setting up state machine.')

    def select_data_dir(self):
        self.data_dir_text.setText(
            QtGui.QFileDialog.getExistingDirectory(self, 'Select data folder',
                                                   data_dir))

    def start_task(self):
        recording = self.test_data_path()
        if recording:
            if not self.fresh_task:
                reset_task = QtGui.QMessageBox.question(
                    self, 'Reset task',
                    'Task has already been run, variables may not have default values.\n\nReset task?',
                    QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
                if reset_task == QtGui.QMessageBox.Yes:
                    self.setup_task()
                    return
            subject_ID = str(self.subject_text.text())
            self.data_logger.open_data_file(self.data_dir, 'run_task',
                                            subject_ID)
        self.fresh_task = False
        self.running = True
        self.board.start_framework()
        self.task_plot.run_start(recording)
        self.task_select.setEnabled(False)
        self.upload_button.setEnabled(False)
        self.file_groupbox.setEnabled(False)
        self.start_button.setEnabled(False)
        self.board_groupbox.setEnabled(False)
        self.stop_button.setEnabled(True)
        self.print_to_log('\nRun started at: {}\n'.format(
            datetime.now().strftime('%Y/%m/%d %H:%M:%S')))
        self.update_timer.start(update_interval)
        self.GUI_main.refresh_timer.stop()
        self.status_text.setText('Running: ' + self.task)
        self.GUI_main.tab_widget.setTabEnabled(
            1, False)  # Disable experiments tab.
        self.GUI_main.tab_widget.setTabEnabled(2, False)  # Disable setups tab.

    def stop_task(self, error=False, stopped_by_task=False):
        self.running = False
        self.update_timer.stop()
        self.GUI_main.refresh_timer.start(self.GUI_main.refresh_interval)
        if not (error or stopped_by_task):
            self.board.stop_framework()
            QtCore.QTimer.singleShot(
                100, self.update)  # Catch output after framework stops.
        self.data_logger.close_files()
        self.task_plot.run_stop()
        self.board_groupbox.setEnabled(True)
        self.file_groupbox.setEnabled(True)
        self.start_button.setEnabled(True)
        self.task_select.setEnabled(True)
        self.upload_button.setEnabled(True)
        self.stop_button.setEnabled(False)
        self.status_text.setText('Uploaded : ' + self.task)
        self.GUI_main.tab_widget.setTabEnabled(1, True)  # Enable setups tab.
        self.GUI_main.tab_widget.setTabEnabled(2, True)  # Enable setups tab.

    # Timer updates

    def update(self):
        # Called regularly during run to process data from board and update plots.
        try:
            self.board.process_data()
            if not self.board.framework_running:
                self.stop_task(stopped_by_task=True)
        except PyboardError:
            self.print_to_log('\nError during framework run.')
            self.stop_task(error=True)
        self.task_plot.update()

    # Cleanup.

    def closeEvent(self, event):
        # Called when GUI window is closed.
        if self.board:
            self.board.stop_framework()
            self.board.close()
        event.accept()

    # Exception handling.

    def excepthook(self, ex_type, ex_value, ex_traceback):
        # Called whenever an uncaught exception occurs.
        if ex_type in (SerialException, SerialTimeoutException):
            self.print_to_log('\nError: Serial connection with board lost.')
        elif ex_type == PyboardError:
            self.print_to_log('\nError: Unable to execute command.')
        else:
            self.print_to_log(
                '\nError: uncaught exception of type: {}'.format(ex_type))
        if self.running:
            self.stop_task(error=True)
        self.disconnect()
示例#3
0
class Setup():
    '''Class representing one setup in the setups table.'''

    def __init__(self, serial_port, setups_tab):
        '''Setup is intilised when board is plugged into computer.'''

        try:
            self.name = setups_tab.saved_names[serial_port]
        except KeyError:
            self.name = serial_port

        self.port = serial_port
        self.setups_tab = setups_tab
        self.board = None

        self.port_item = QtGui.QTableWidgetItem()
        self.port_item.setText(serial_port)
        self.port_item.setFlags(QtCore.Qt.ItemIsEnabled)

        self.name_item = QtGui.QTableWidgetItem()
        self.name_item.changed = self.name_edited
        if self.name != self.port: 
            self.name_item.setText(self.name)

        self.select_checkbox = TableCheckbox()
        self.config_button = QtGui.QPushButton('Configure')
        self.config_button.clicked.connect(self.open_config_dialog)

        self.setups_tab.setups_table.insertRow(0)
        self.setups_tab.setups_table.setItem(0, 0, self.port_item)
        self.setups_tab.setups_table.setItem(0, 1, self.name_item)
        self.setups_tab.setups_table.setCellWidget(0, 2, self.select_checkbox)
        self.setups_tab.setups_table.setCellWidget(0, 3, self.config_button)

    def name_edited(self):
        '''If name entry in table is blank setup name is set to serial port.'''
        name = str(self.name_item.text())
        self.name = name if name else self.port
        self.setups_tab.update_available_setups()
        self.setups_tab.update_saved_setups(self)

    def open_config_dialog(self):
        '''Open the config dialog and update board status as required.'''
        if not self.board: self.connect()
        if self.board:
            self.setups_tab.GUI_main.config_dialog.exec_(self.board)
            if self.setups_tab.GUI_main.config_dialog.disconnect:
                self.disconnect()

    def print(self, print_string):
        ''' Print a string to the log prepended with the setup name.'''
        self.setups_tab.print_to_log('\n{}: '.format(self.name) + print_string)

    def connect(self):
        '''Instantiate pyboard object, opening serial connection to board.'''
        self.print('Connecting to board.')
        try: 
            self.board = Pycboard(self.port, print_func=self.setups_tab.print_to_log)
        except PyboardError:
            self.print('Unable to connect.')

    def disconnect(self):
        
        if self.board:
            self.board.close()
            self.board = None

    def unplugged(self):
        '''Called when a board is physically unplugged from computer. 
        Closes serial connection and removes row from setups table.'''
        if self.board: self.board.close()
        self.setups_tab.setups_table.removeRow(self.port_item.row())
        del(self.setups_tab.setups[self.port])

    def load_framework(self):
        if not self.board: self.connect()
        if self.board:
            self.print('Loading framework.')
            self.board.load_framework()

    def load_hardware_definition(self, hwd_path):
        if not self.board: self.connect()
        if self.board:
            self.print('Loading hardware definition.')
            self.board.load_hardware_definition(hwd_path)

    def enable_flashdrive(self):
        if not self.board: self.connect()
        if self.board:
            self.print('Enabling flashdrive.')
            self.board.enable_mass_storage()
            self.board.close()
            self.board = None

    def disable_flashdrive(self):
        if not self.board: self.connect()
        if self.board:
            self.print('Disabling flashdrive.')
            self.board.disable_mass_storage()
            self.board.close()
            self.board = None