def open(self, host='', port=45454, timeout=0.01): """ Opens a socket connection to specified host and port :param host: Host address to connect to. :type host: str. :param port: Port number to connect to. :type port: int. :param timeout: Sets timeout for socket interactions. :type timeout: float. :return: True if the connection was open. :rtype: bool. """ try: # self._socket_server.timeout = timeout port = int(port) self._socket_server.bind((host, port)) Log.i(TAG, "Socket open {}:{}".format(host, port)) print("listening on {}:{}".format( self._socket_server.getsockname()[0], self._socket_server.getsockname()[1])) self._socket_server.listen(1) self._conn, address = self._socket_server.accept() self._conn.setblocking(0) # non-blocking print("accepted from {}".format(address)) return True except socket.timeout: Log.w(TAG, "Connection timeout") return False
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) else: self._parser_process = ParserProcess(self._queue) if self._source == SourceType.serial: self._acquisition_process = SerialProcess(self._parser_process) elif self._source == SourceType.simulator: self._acquisition_process = SimulatorProcess(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, data_queue, store_reference=None, split=Constants.csv_delimiter, consumer_timeout=Constants.parser_timeout_ms): """ :param data_queue: Reference to Queue where processed data will be put. :type data_queue: multiprocessing Queue. :param store_reference: Reference to CSVProcess instance, if needed. :type store_reference: CSVProcess (multiprocessing.Process) :param split: Delimiter in incoming data. :type split: str. :param consumer_timeout: Time to wait after emptying the internal buffer before next parsing. :type consumer_timeout: float. """ mp.Process.__init__(self) self._exit = mp.Event() self._in_queue = mp.Queue() self._out_queue = data_queue # Queue from the worker process self._consumer_timeout = consumer_timeout self._split = split self._store_reference = store_reference self._leftover = '' Log.d(TAG, "Process ready")
def stop(self): """ Signals the process to stop acquiring data. :return: """ Log.i(TAG, "Server closing") self._conn.close() self._socket_server.close() self._exit.set()
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())
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.stop()
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()
def set_user_log_level(self): """ Sets the user specified log level. :return: """ if self._parser is not None: self._parse_log_level() else: Log.w(TAG, "Parser was not created !") return None
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._socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) Log.i(TAG, "Process Ready")
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 run(self): """ Process will monitor the internal buffer to parse raw data and distribute to graph and storage, if needed. The process will loop again after timeout if more data is available. :return: """ Log.d(TAG, "Process starting...") while not self._exit.is_set(): self._consume_queue() sleep(self._consumer_timeout) # last check on the queue to completely remove data. self._consume_queue() Log.d(TAG, "Process finished")
def consume_queue(self): """ Empties the internal queue, updating data to consumers. :return: """ # i = 0 while True: try: # i += 1 self._store_data(self._queue.get(block=False)) except queue.Empty: # print("parsed queue: ", i) return Log.d(TAG, f"queue len: {i}")
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): """ Simulates raw data incoming as CSV. :return: """ Log.i(TAG, "Process starting...") timestamp = time() coef = 2 * np.pi while not self._exit.is_set(): stamp = time() - timestamp self._parser.add([stamp, str(("{},{}\r\n".format(np.sin(coef * stamp), np.cos(coef * stamp)))) .encode(Constants.app_encoding)]) sleep(self._period) Log.i(TAG, "Process finished")
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._socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._conn = None Log.i(TAG, "Process Ready")
def open(self, port=None, speed=Constants.simulator_default_speed, timeout=0.5): """ Opens a specified serial port. :param port: Not used. :type port: str. :param speed: Period of the generated signal. :type speed: float. :param timeout: Not used. :type timeout: float. :return: True if the port is available. :rtype: bool. """ self._period = float(speed) Log.i(TAG, "Using sample rate at {}".format(self._period)) return True
def run(self): if Architecture.is_python_version(MinimalPython.major, minor=MinimalPython.minor): Log.i(TAG, "Starting RTGraph") 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 RTGraph\n") win.close() else: self._fail() self.close()
def get_source_speeds(source): """ Gets the available speeds for specified source. :param source: Source to get available speeds. :type source: SourceType. :return: List of available speeds. :rtype: str list. """ if source == SourceType.serial: return SerialProcess.get_speeds() elif source == SourceType.simulator: return SimulatorProcess.get_speeds() else: Log.w(TAG, "Unknown source selected") return None
def get_ports(): """ Gets a list of the available serial ports. :return: List of available serial ports. :rtype: str list. """ if Architecture.get_os() is OSType.macosx: import glob return glob.glob("/dev/tty.*") else: found_ports = [] for port in list(list_ports.comports()): Log.d(TAG, "found device {}".format(port)) found_ports.append(port.device) return found_ports
def get_source_ports(source): """ Gets the available ports for specified source. :param source: Source to get available ports. :type source: SourceType. :return: List of available ports. :rtype: str list. """ if source == SourceType.serial: return SerialProcess.get_ports() elif source == SourceType.simulator: return SimulatorProcess.get_ports() elif source == SourceType.SocketServer: return SocketProcess.get_default_host() else: Log.w(TAG, "Unknown source selected") return None
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) 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 _source_changed(self): """ Updates the source and depending boxes on change. This function is connected to the indexValueChanged signal of the Source ComboBox. :return: """ Log.i(TAG, "Scanning source {}".format(self._get_source().name)) # clear boxes before adding new self.ui.cBox_Port.clear() self.ui.cBox_Speed.clear() source = self._get_source() ports = self.worker.get_source_ports(source) speeds = self.worker.get_source_speeds(source) self.ui.cBox_Port.addItems(ports) self.ui.cBox_Speed.addItems(speeds) self.ui.cBox_Speed.setCurrentIndex(len(speeds) - 1)
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=path) if not FileManager.file_exists(full_path): Log.i(TAG, "Storing in {}".format(full_path)) return open(full_path, "a", newline='') 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.worker = Worker(port=self.ui.cBox_Port.currentText(), speed=float(self.ui.cBox_Speed.currentText()), samples=self.ui.sBox_Samples.value(), source=self._get_source(), export_enabled=self.ui.chBox_export.isChecked()) 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()))
def open(self, port='', speed=45454, timeout=0.01): """ Opens a socket connection to specified host and port :param port: Host address to connect to. :type port: str. :param speed: Port number to connect to. :type speed: int. :param timeout: Sets timeout for socket interactions. :type timeout: float. :return: True if the connection was open. :rtype: bool. """ try: #self._socket_client.timeout = timeout speed = int(speed) self._socket_client.connect((port, speed)) Log.i(TAG, "Socket open {}:{}".format(port, speed)) return True except socket.timeout: Log.w(TAG, "Connection timeout") return False
def _parse_csv(self, time, line): """ Parses incoming data and distributes to external processes. :param time: Timestamp. :type time: float. :param line: Raw data coming from acquisition process. :type line: basestring. :return: """ if len(line) > 0: try: if type(line) == bytes: values = line.decode("UTF-8").split(self._split) elif type(line) == str: values = line.split(self._split) else: raise TypeError values = [float(v) for v in values] Log.d(TAG, values) self._out_queue.put((time, values)) if self._store_reference is not None: self._store_reference.add(time, values) except ValueError: Log.w(TAG, "Can't convert to float. Raw: {}".format(line.strip())) except AttributeError: Log.w(TAG, "Attribute error on type ({}). Raw: {}".format(type(line), line.strip()))
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 = strftime(Constants.csv_default_filename, gmtime()) self._file = 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")
def run(self): """ Reads the socket until a stop call is made. :return: """ Log.i(TAG, "Process starting...") timestamp = time() while not self._exit.is_set(): stamp = time() - timestamp try: data = self._socket_client.recv( Constants.SocketClient.buffer_recv_size).decode() if len(data) > 0: self._parser.add([stamp, data]) except socket.timeout: Log.w(TAG, "read timeout") Log.i(TAG, "Process finished")