class SerialConnection(Connection): # Serial Constants BYTESIZE = { 5: serial.FIVEBITS, 6: serial.SIXBITS, 7: serial.SEVENBITS, 8: serial.EIGHTBITS } PARITY = { 'N': serial.PARITY_NONE, 'E': serial.PARITY_EVEN, 'O': serial.PARITY_ODD } STOPBITS = {1: serial.STOPBITS_ONE, 2: serial.STOPBITS_TWO} def __init__(self): Connection.__init__(self) # New data available notifier self.notifier = None def begin(self, instrument): self.log = logging.getLogger('GDAIS.' + instrument.short_name + '.SerialConnection') self.io_conn = serial.Serial() self.io_conn.port = instrument.connection.serial_port self.io_conn.baudrate = instrument.connection.baudrate self.io_conn.bytesize = self.BYTESIZE[instrument.connection.data_bits] self.io_conn.parity = self.PARITY[instrument.connection.parity] self.io_conn.stopbits = self.STOPBITS[instrument.connection.stop_bits] self.io_conn.timeout = 0 # non-blocking mode (return immediately on read) try: self.io_conn.open() except serial.SerialException: self.log.exception("Serial device can not be found or configured") self.error_occurred.emit() else: Connection.begin(self, instrument) def run(self): self.notifier = QSocketNotifier(self.io_conn.fileno(), QSocketNotifier.Read) self.notifier.activated.connect(self.read_data) Connection.run(self) def quit(self): if self.notifier and self.notifier.isEnabled(): self.notifier.setEnabled(False) if self.io_conn and self.io_conn.isOpen(): self.log.debug("Clearing serial port buffers (In and Out)") self.io_conn.flushOutput() self.io_conn.flushInput() self.log.info("Closing serial port") self.io_conn.close() Connection.quit(self)
class QJsonRpcClient(QObject): """A JSON-RPC client integrated with the Qt event loop.""" default_timeout = 5 def __init__(self, message_handler=None, timeout=-1, parent=None): """Create a new message bus connection. The *handler* specifies an optional message handler. """ super(QJsonRpcClient, self).__init__(parent) self._message_handler = message_handler self._timeout = timeout if timeout != -1 else self.default_timeout self._socket = None self._method_calls = {} self._outbuf = b"" self._incoming = collections.deque() self._outgoing = collections.deque() self._protocol = jsonrpc.JsonRpcProtocol(True) self._read_notifier = None self._write_notifier = None self._log = logging.get_logger(self) @property def timeout(self): return self._timeout def connect(self, address): """Connect to a JSON-RPC server at *address*.""" if isinstance(address, socket.socket): sock = address else: sock = util.create_connection(address, self._timeout) sock.settimeout(0) self._read_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Read, self) self._read_notifier.activated.connect(self._do_read) self._read_notifier.setEnabled(True) self._write_notifier = QSocketNotifier(sock.fileno(), QSocketNotifier.Write, self) self._write_notifier.activated.connect(self._do_write) self._write_notifier.setEnabled(False) self._socket = sock def _do_read(self): # Read messages from the socket and put them into the incoming queue # until nothing more can be read. while True: try: buf = self._socket.recv(4096) except socket.error as e: if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK): break self._log.error("recv() error {0}".format(e.errno)) self.close() break if buf == b"": self._log.error("peer closed connection") self.close() break # XXX: should not be using protocol private attributes # Expose .error and .get_message() ? nbytes = self._protocol.data_received(buf) if self._protocol._error: self._log.error("parse error {0!s}", self._protocol._error) self.close() break while self._protocol._queue.qsize(): message = self._protocol._queue.get(block=False) self._incoming.append(message) # Schedule a dispatch if there are incoming messages if self._incoming: QCoreApplication.instance().postEvent(self, _Dispatch()) def _do_write(self): # Drain message from the outgoing queue until we would block or until # the queue is empty. while True: if not self._outbuf: if not self._outgoing: break message = self._outgoing.popleft() self._outbuf = json.dumps(message).encode("utf8") try: nbytes = self._socket.send(self._outbuf) except socket.error as e: if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK): break self.logger.error("send() error {0}".format(e.errno)) self.close() break self._outbuf = self._outbuf[nbytes:] if not self._outbuf: self._write_notifier.setEnabled(False) def close(self): """Close the connection.""" if self._socket is None: return self._read_notifier.setEnabled(False) self._write_notifier.setEnabled(False) try: self._socket.close() except socket.error: pass self._log.debug("connection closed") self._socket = None def send_message(self, message): """Send a raw JSON-RPC message.""" if self._socket is None: raise RuntimeError("not connected") if not jsonrpc.check_message(message): raise ValueError("invalid JSON-RPC message") self._outgoing.append(message) if not self._write_notifier.isEnabled(): self._write_notifier.setEnabled(True) def send_notification(self, method, *args): """Send a JSON-RPC notification.""" message = jsonrpc.create_notification(method, args) self.send_message(message) def event(self, event): # Process the DispatchMessages event if isinstance(event, _Dispatch): self._dispatch() event.accept() return True else: event.ignore() return False def _dispatch(self): # Dispatch message from the connection. while self._incoming: message = self._incoming.popleft() if "result" in message or "error" in message: # response key = message["id"] callback = self._method_calls.get(key, None) if callback: callback(message, self) elif self._message_handler: self._message_handler(message, self) else: self._log.info("no handler, cannot handle incoming message") def call_method(self, method, *args, **kwargs): """Call a method.""" # XXX: limiting the recusion depth needs more thought if len(self._method_calls) > 5: raise RuntimeError("recursion level too deep") message = jsonrpc.create_request(method, args) self.send_message(message) replies = [] def method_response(message, client): replies.append(message) def method_timeout(): reply = jsonrpc.create_error(message, jsonrpc.SERVER_ERROR, "Method call timed out") replies.append(reply) timeout = kwargs.pop("timeout", self.timeout) if timeout: timer = QTimer(self) timer.setInterval(timeout * 1000) timer.setSingleShot(True) timer.timeout.connect(method_timeout) timer.start() # Run an embedded event loop to process network events until we get a # response. We limit the call depth so that we don't run the risk of # overflowing the stack. self._method_calls[message["id"]] = method_response loop = QEventLoop() mask = QEventLoop.ExcludeUserInputEvents | QEventLoop.WaitForMoreEvents while True: loop.processEvents(mask) if replies: break if timeout: timer.stop() reply = replies[0] del self._method_calls[message["id"]] if reply.get("error"): raise QJsonRpcError(reply["error"]) self.message = reply return reply.get("result")