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()
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