def save(self): """ Saves the recorded file to the desired location. :return: """ Log.i(TAG, "Clicked save") if len(self.session_files) > 0: save_path = PopUp.save_file(self) if save_path != '': Log.i( TAG, "Saving {} at {}".format(self.session_files[-1], save_path)) shutil.copyfile(self.session_files[-1], save_path) Log.i( TAG, "Saved {} at {}".format(self.session_files[-1], save_path)) self.ui.tBrowser_Log.append( "Saved data at {}.".format(save_path)) else: Log.i(TAG, "No file name entered, so file wasn't saved.") else: Log.i(TAG, "No signal files in current session.") self.ui.tBrowser_Log.append("Terminated save due to an error.") PopUp.warning(self, Constants.app_title, "Record signal before saving.")
def start(self): """ Starts all processes, based on configuration given in constructor. :return: """ self.reset_buffers(self._samples) if self._export: self._csv_process = CSVProcess(path=self._path) self._parser_process = ParserProcess( self._queue, store_reference=self._csv_process) self._filepath = self._csv_process.get_filepath() else: self._parser_process = ParserProcess(self._queue) if self._source == SourceType.serial: self._acquisition_process = SerialProcess(self._parser_process) if self._acquisition_process.open(port=self._port, speed=self._speed): self._parser_process.start() if self._export: self._csv_process.start() self._acquisition_process.start() return True else: Log.i(TAG, "Port is not available") return False
def stop(self): """ Signals the process to stop acquiring data. :return: """ Log.i(TAG, "Process finishing...") self._exit.set()
def __init__(self, parser_process): """ Initialises values for process. :param parser_process: Reference to a ParserProcess instance. :type parser_process: ParserProcess. """ multiprocessing.Process.__init__(self) self._exit = multiprocessing.Event() self._parser = parser_process self._serial = serial.Serial() Log.i(TAG, "Process ready")
def _update_sample_size(self): """ Updates the sample size of the plot. This function is connected to the valueChanged signal of the sample Spin Box. :return: """ if self.worker is not None: Log.i(TAG, "Changing sample size") self.worker.reset_buffers(self.ui.sBox_Samples.value()) self.ui.tBrowser_Log.append("Updated sample size: {}.".format( self.ui.sBox_Samples.value()))
def closeEvent(self, evnt): """ Overrides the QTCloseEvent. This function is connected to the clicked signal of the close button of the window. :param evnt: QT evnt. :return: """ if self.worker.is_running(): Log.i(TAG, "Window closed without stopping capture, stopping it") self.ui.tBrowser_Log.append("Closing window.") self.stop()
def help(self): """ Opens the message for the program. This function is connected to the clicked signal of the Help button. :return: """ Log.i(TAG, "Clicked help") PopUp.message( self, Constants.app_title + " Instructions", "View Options:\n - Start: starts data stream\n - Stop: stops data stream\n\nRecord Options\n - n Seconds: enter the desired time length\n - Record: starts recording data\n - Save: save recorded data\n\nBCI/Plot Options\n - Ports: select the serial device port\n - Baud Rate: select the baud rate\n - n Samples: enter the desired samples #" )
def _update_seconds(self): """ Updates the seconds for recording. This function is connected to the valueChanged signal of the sample Spin Box. :return: """ if self.worker is not None: Log.i(TAG, "Changing recording length") self.worker.reset_buffers(self.ui.sBox_Seconds.value()) self.ui.tBrowser_Log.append( "Updated recording length: {} seconds.".format( self.ui.sBox_Seconds.value()))
def _update_baud(self): """ Updates the baud rate. :return: """ if self.ui.cBox_BaudRate.currentText() == "Baud Rate": Log.i(TAG, "No baud rate selected") self.ui.tBrowser_Log.append("No baud rate was selected.") else: Log.i( TAG, "Updated baud rate: {}".format( self.ui.cBox_BaudRate.currentText())) self.ui.tBrowser_Log.append("Updated baud rate: {}.".format( self.ui.cBox_BaudRate.currentText()))
def reset_buffers(self, samples): """ Setup/clear the internal buffers. :param samples: Number of samples for the buffers. :type samples: int. :return: """ self._data_buffers = [] for tmp in Constants.plot_colors: self._data_buffers.append(RingBuffer(samples)) self._time_buffer = RingBuffer(samples) while not self._queue.empty(): self._queue.get() Log.i(TAG, "Buffers cleared")
def run(self): if Architecture.is_python_version(MinimalPython.major, minor=MinimalPython.minor): Log.i(TAG, "Starting mbci_lab") win = mainWindow.MainWindow(samples=self._args.get_user_samples()) win.setWindowTitle("{} - {}".format(Constants.app_title, Constants.app_version)) win.show() self._app.exec() Log.i(TAG, "Finishing mbci_lab\n") win.close() else: self._fail() self.close()
def stop(self): """ Stops the acquisition of the selected serial port. This function is connected to the clicked signal of the Stop button. :return: """ Log.i(TAG, "Clicked stop") self._timer_plot.stop() self._enable_ui(True) self.worker.stop() self.session_files.append(self.worker.get_filepath()) if self._isrecording == True: self._isrecording = False self.save() self.ui.tBrowser_Log.append("Stopped data stream.")
def run(self): """ Process will monitor the internal buffer to write data to the export file, and the process will loop again after timeout if more data is available. :return: """ Log.i(TAG, "Process starting...") self._csv = csv.writer(self._file, delimiter=Constants.csv_delimiter, quoting=csv.QUOTE_MINIMAL) self._csv.writerow(['time', 'signal']) while not self._exit.is_set(): self._consume_queue() sleep(self._timeout) # last check on the queue to completely remove data. self._consume_queue() Log.i(TAG, "Process finished") self._file.close()
def _create_file(filename, path=None, extension=Constants.csv_extension): """ Creates the file to export the data. :param filename: Name of the file where data will be exported. :type filename: str. :param path: Path where data file will be saved. :type path: str. :param extension: Extension to give to the export file. :type extension: str. :return: Reference to the export file. """ FileManager.create_dir(path) full_path = FileManager.create_file(filename, extension=extension, path=os.path.abspath(path)) if not FileManager.file_exists(full_path): Log.i(TAG, "Storing in {}".format(full_path)) return open(full_path, "a", newline=''), full_path return None
def start(self): """ Starts the acquisition of the selected serial port. This function is connected to the clicked signal of the Start button. :return: """ Log.i(TAG, "Clicked start") self.ui.tBrowser_Log.append("Started data stream.") port_id, baud_rate = self.ui.cBox_Port.currentText( ), self.ui.cBox_BaudRate.currentText() if (port_id != "Ports (Refresh)") and (baud_rate != "Baud Rate"): self.worker = Worker(port=self.ui.cBox_Port.currentText(), speed=int( self.ui.cBox_BaudRate.currentText()), samples=self.ui.sBox_Samples.value()) if self.worker.start(): self._timer_plot.start(Constants.plot_update_ms) self._enable_ui(False) else: Log.i(TAG, "Port is not available") PopUp.warning( self, Constants.app_title, "Selected port \"{}\" is not available".format( self.ui.cBox_Port.currentText())) else: Log.i(TAG, "No port or baud rate was selected") self.ui.tBrowser_Log.append("Terminated data stream due to error.") PopUp.warning(self, Constants.app_title, "Select a port and baud rate")
def record(self): """ Starts recording signal from the selected port. Function is connected to the record button :return: """ Log.i(TAG, "Clicked record") self.ui.tBrowser_Log.append("Started recording data.") self._isrecording = True port_id, baud_rate = self.ui.cBox_Port.currentText( ), self.ui.cBox_BaudRate.currentText() if (port_id != "Ports (Refresh)") and (baud_rate != "Baud Rate"): self.worker = Worker(port=self.ui.cBox_Port.currentText(), speed=int( self.ui.cBox_BaudRate.currentText()), samples=self.ui.sBox_Samples.value(), export_enabled=True) if self.worker.start(): self._timer_plot.start(Constants.plot_update_ms) self._enable_ui(False) else: Log.i(TAG, "Port is not available") PopUp.warning( self, Constants.app_title, "Selected port \"{}\" is not available".format( self.ui.cBox_Port.currentText())) return None else: Log.i(TAG, "No port or baud rate was selected") self.ui.tBrowser_Log.append( "Terminated data recording due to error.") PopUp.warning(self, Constants.app_title, "Select a port and baud rate") return None
def _update_port(self): """ Updates the source and depending boxes on change. This function is connected to the indexValueChanged signal of the Source ComboBox. :return: """ tmp = self.ui.cBox_Port.currentText() if tmp == "Ports (Refresh)": Log.i(TAG, "Scanning source serial") # clear boxes before adding new self.ui.cBox_Port.clear() ports = self.worker.get_source_ports(SourceType.serial) ports.insert(0, "Ports (Refresh)") if ports is not None: self.ui.cBox_Port.addItems(ports) self.ui.tBrowser_Log.append("Refreshed ports.") else: Log.i(TAG, "Serial port {} was selected".format(tmp)) self.ui.tBrowser_Log.append("Selected port: {}".format( self.ui.cBox_Port.currentText()))
def __init__(self, filename=None, path=None, timeout=0.5): """ Sets up the file to export the data as CSV. If filename is not specified, a default name based on time will be used. :param filename: Name of the file where data will be exported. :type filename: str. :param path: Path where data file will be saved. :type path: str. :param timeout: Time to wait after emptying the internal buffer before next write. :type timeout: float. """ multiprocessing.Process.__init__(self) self._exit = multiprocessing.Event() self._store_queue = multiprocessing.Queue() self._csv = None self._file = None self._timeout = timeout if filename is None: filename = 'temp_{}'.format( strftime(Constants.csv_default_filename, gmtime())) self._file, self._filepath = self._create_file(filename, path=path) Log.i(TAG, "Process ready")
def run(self): """ Reads the serial port expecting CSV until a stop call is made. The expected format is comma (",") separated values, and a new line (CRLF or LF) as a new row. While running, it will parse CSV data convert each value to float and added to a queue. If incoming data from serial port can't be converted to float, that data will be discarded. :return: """ Log.i(TAG, "Process starting...") if self._is_port_available(self._serial.port): if not self._serial.isOpen(): self._serial.open() Log.i(TAG, "Port opened") timestamp = time() while not self._exit.is_set(): self._parser.add( [time() - timestamp, self._serial.readline()]) Log.i(TAG, "Process finished") self._serial.close() else: Log.w(TAG, "Port is not opened") else: Log.w(TAG, "Port is not available")