using serial port in python3 asyncio

后端 未结 8 2030
长情又很酷
长情又很酷 2021-02-08 09:39

i\'m trying and, so far, failing to use python asyncio to access a serial port.

i\'d really appreciate any tips on using the new python async framework on a simple fd.

8条回答
  •  说谎
    说谎 (楼主)
    2021-02-08 10:02

    I wrote an AsyncFile class a while ago, the interface is easier then low-level protocols.

    The original code is here: https://github.com/l04m33/pyx/blob/dbaf121ab7bb9bbf04616a7285bcaba757682d03/pyx/io.py#L20

    class AsyncFile:
        """A local file class for use with the ``asyncio`` module.
        ``loop`` should be the event loop in use.
        ``filename`` is the name of the file to be opened.
        ``fileobj`` should be a regular file-like object.
        ``mode`` is the open mode accepted by built-in function ``open``.
        If ``filename`` is specified, the named file will be opened. And if
        ``fileobj`` is specified, that file object will be used directly. You
        cannot specify both ``filename`` and ``fileobj``.
        This class can be used in a ``with`` statement.
        """
    
        DEFAULT_BLOCK_SIZE = 8192
    
        def __init__(self, loop=None, filename=None,
                     fileobj=None, mode='rb'):
            if (filename is None and fileobj is None) or \
                    (filename is not None and fileobj is not None):
                raise RuntimeError('Confilicting arguments')
    
            if filename is not None:
                if 'b' not in mode:
                    raise RuntimeError('Only binary mode is supported')
                fileobj = open(filename, mode=mode)
            elif 'b' not in fileobj.mode:
                raise RuntimeError('Only binary mode is supported')
    
            fl = fcntl.fcntl(fileobj, fcntl.F_GETFL)
            if fcntl.fcntl(fileobj, fcntl.F_SETFL, fl | os.O_NONBLOCK) != 0:
                if filename is not None:
                    fileobj.close()
                errcode = ctypes.get_errno()
                raise OSError((errcode, errno.errorcode[errcode]))
    
            self._fileobj = fileobj
    
            if loop is None:
                loop = asyncio.get_event_loop()
            self._loop = loop
            self._rbuffer = bytearray()
    
        def __enter__(self):
            return self
    
        def __exit__(self, exc_type, exc_value, traceback):
            self.close()
    
        def fileno(self):
            return self._fileobj.fileno()
    
        def seek(self, offset, whence=None):
            if whence is None:
                return self._fileobj.seek(offset)
            else:
                return self._fileobj.seek(offset, whence)
    
        def tell(self):
            return self._fileobj.tell()
    
        def _read_ready(self, future, n, total):
            if future.cancelled():
                self._loop.remove_reader(self._fileobj.fileno())
                return
    
            try:
                res = self._fileobj.read(n)
            except (BlockingIOError, InterruptedError):
                return
            except Exception as exc:
                self._loop.remove_reader(self._fileobj.fileno())
                future.set_exception(exc)
                return
    
            if not res:     # EOF
                self._loop.remove_reader(self._fileobj.fileno())
                future.set_result(bytes(self._rbuffer))
                return
    
            self._rbuffer.extend(res)
    
            if total > 0:
                more_to_go = total - len(self._rbuffer)
                if more_to_go <= 0:  # enough
                    res, self._rbuffer = self._rbuffer[:n], self._rbuffer[n:]
                    self._loop.remove_reader(self._fileobj.fileno())
                    future.set_result(bytes(res))
                else:
                    more_to_go = min(self.DEFAULT_BLOCK_SIZE, more_to_go)
                    self._loop.add_reader(self._fileobj.fileno(),
                                          self._read_ready,
                                          future, more_to_go, total)
            else:   # total < 0
                # This callback is still registered with total < 0,
                # nothing to do here
                pass
    
        @asyncio.coroutine
        def read(self, n=-1):
            future = asyncio.Future(loop=self._loop)
    
            if n == 0:
                future.set_result(b'')
            else:
                try:
                    res = self._fileobj.read(n)
                except (BlockingIOError, InterruptedError):
                    if n < 0:
                        self._rbuffer.clear()
                        self._loop.add_reader(self._fileobj.fileno(),
                                              self._read_ready,
                                              future, self.DEFAULT_BLOCK_SIZE, n)
                    else:
                        self._rbuffer.clear()
                        read_block_size = min(self.DEFAULT_BLOCK_SIZE, n)
                        self._loop.add_reader(self._fileobj.fileno(),
                                              self._read_ready,
                                              future, read_block_size, n)
                except Exception as exc:
                    future.set_exception(exc)
                else:
                    future.set_result(res)
    
            return future
    
        def _write_ready(self, future, data, written):
            if future.cancelled():
                self._loop.remove_writer(self._fileobj.fileno())
                return
    
            try:
                res = self._fileobj.write(data)
            except (BlockingIOError, InterruptedError):
                return
            except Exception as exc:
                self._loop.remove_writer(self._fileobj.fileno())
                future.set_exception(exc)
                return
    
            if res < len(data):
                data = data[res:]
                self._loop.add_writer(self._fileobj.fileno(),
                                      self._write_ready,
                                      future, data, written + res)
            else:
                self._loop.remove_writer(self._fileobj.fileno())
                future.set_result(written + res)
    
        @asyncio.coroutine
        def write(self, data):
            future = asyncio.Future(loop=self._loop)
    
            if len(data) == 0:
                future.set_result(0)
            else:
                try:
                    res = self._fileobj.write(data)
                except (BlockingIOError, InterruptedError):
                    self._loop.add_writer(self._fileobj.fileno(),
                                          self._write_ready,
                                          future, data, 0)
                except Exception as exc:
                    future.set_exception(exc)
                else:
                    future.set_result(res)
    
            return future
    
        def stat(self):
            return os.stat(self._fileobj.fileno(), follow_symlinks=True)
    
        def close(self):
            self._loop.remove_reader(self._fileobj.fileno())
            self._loop.remove_writer(self._fileobj.fileno())
            self._fileobj.close()
    

提交回复
热议问题