plesty.lib.traffic.apt_d2xx =========================== .. py:module:: plesty.lib.traffic.apt_d2xx .. autoapi-nested-parse:: Reach an APT controller through FTDI's D2XX driver when it has no COM port. On a Windows host with Thorlabs Kinesis installed, an APT controller is bound to the FTDI D2XX driver and exposes no virtual COM port, so ``serial.tools.list_ports`` never lists it. Kinesis itself talks D2XX, and so can :class:`~plesty.lib.traffic.apt.AptTrafficManager`: :class:`D2xxPort` is a serial-like adapter over FTDI's D2XX library exposing exactly what the manager calls on a port. The library is FTDI's own ``ftd2xx.dll``, called through :mod:`ctypes`: it is installed with the driver — by Kinesis, or FTDI's driver package — on every host where a controller is bound to D2XX, so nothing extra is installed in Python (#68). Without it this module reports no devices rather than failing to import. Enumerating (:func:`list_devices`) reads the driver's device list and never opens a controller. Opening one (:meth:`D2xxPort.open`) needs Kinesis closed: it holds the device while it runs. Attributes ---------- .. autoapisummary:: plesty.lib.traffic.apt_d2xx.ADDRESS_PREFIX plesty.lib.traffic.apt_d2xx.BAUDRATE plesty.lib.traffic.apt_d2xx.WORD_LENGTH_8 plesty.lib.traffic.apt_d2xx.STOP_BITS_1 plesty.lib.traffic.apt_d2xx.PARITY_NONE plesty.lib.traffic.apt_d2xx.FLOW_RTS_CTS plesty.lib.traffic.apt_d2xx.PURGE_RX plesty.lib.traffic.apt_d2xx.PURGE_TX plesty.lib.traffic.apt_d2xx._PURGE_DWELL plesty.lib.traffic.apt_d2xx._IO_TIMEOUT_MS plesty.lib.traffic.apt_d2xx._LIBRARY_NAMES plesty.lib.traffic.apt_d2xx._OPEN_BY_SERIAL_NUMBER plesty.lib.traffic.apt_d2xx._INFO_BUFFER plesty.lib.traffic.apt_d2xx._HANDLE plesty.lib.traffic.apt_d2xx._DWORD_P plesty.lib.traffic.apt_d2xx._PROTOTYPES plesty.lib.traffic.apt_d2xx._STATUS Classes ------- .. autoapisummary:: plesty.lib.traffic.apt_d2xx.D2xxDevice plesty.lib.traffic.apt_d2xx._D2xxHandle plesty.lib.traffic.apt_d2xx._D2xxLibrary plesty.lib.traffic.apt_d2xx.D2xxPort Functions --------- .. autoapisummary:: plesty.lib.traffic.apt_d2xx.is_d2xx_address plesty.lib.traffic.apt_d2xx.d2xx_serial plesty.lib.traffic.apt_d2xx._decode plesty.lib.traffic.apt_d2xx.load_driver plesty.lib.traffic.apt_d2xx.list_devices Module Contents --------------- .. py:data:: ADDRESS_PREFIX :value: 'd2xx:' .. py:data:: BAUDRATE :value: 115200 .. py:data:: WORD_LENGTH_8 :value: 8 .. py:data:: STOP_BITS_1 :value: 0 .. py:data:: PARITY_NONE :value: 0 .. py:data:: FLOW_RTS_CTS :value: 256 .. py:data:: PURGE_RX :value: 1 .. py:data:: PURGE_TX :value: 2 .. py:data:: _PURGE_DWELL :value: 0.05 .. py:data:: _IO_TIMEOUT_MS :value: 1000 .. py:class:: D2xxDevice One controller as the D2XX driver lists it. :ivar serial: The serial number, as the driver reports it. :ivar description: The driver's description, e.g. ``Kinesis K-Cube Stepper``. .. py:attribute:: serial :type: str .. py:attribute:: description :type: str :value: '' .. py:property:: address :type: str ``d2xx:``. :type: The address that opens this controller .. py:function:: is_d2xx_address(address: str | None) -> bool Return whether *address* names a controller over D2XX (``d2xx:``). .. py:function:: d2xx_serial(address: str) -> str Return the serial number of a ``d2xx:`` address. .. py:data:: _LIBRARY_NAMES .. py:data:: _OPEN_BY_SERIAL_NUMBER :value: 1 .. py:data:: _INFO_BUFFER :value: 256 .. py:data:: _HANDLE .. py:data:: _DWORD_P .. py:data:: _PROTOTYPES :type: dict[str, list[Any]] .. py:data:: _STATUS :value: ('FT_OK', 'FT_INVALID_HANDLE', 'FT_DEVICE_NOT_FOUND', 'FT_DEVICE_NOT_OPENED', 'FT_IO_ERROR',... .. py:function:: _decode(value: bytes) -> str Return a driver string — NUL-terminated ASCII — as text. .. py:class:: _D2xxHandle(library: _D2xxLibrary, handle: ctypes.c_void_p) One opened device: the D2XX calls :class:`D2xxPort` makes on it. .. py:attribute:: _lib .. py:attribute:: _handle .. py:method:: _call(name: str, *args: Any) -> None .. py:method:: set_timeouts(read_ms: int, write_ms: int) -> None .. py:method:: set_baud_rate(baud: int) -> None .. py:method:: set_data_characteristics(word_length: int, stop_bits: int, parity: int) -> None .. py:method:: purge(mask: int) -> None .. py:method:: reset_device() -> None .. py:method:: set_flow_control(flow: int, xon: int, xoff: int) -> None .. py:method:: set_rts() -> None .. py:method:: queue_status() -> int .. py:method:: write(data: bytes) -> int .. py:method:: read(size: int) -> bytes .. py:method:: close() -> None .. py:class:: _D2xxLibrary(dll: Any) FTDI's D2XX library, loaded through :mod:`ctypes`. Wrap *dll* and declare the prototypes of the functions it is asked for. :param dll: The loaded library — or, in a test, anything with the same function names; only real ctypes functions take a prototype. .. py:attribute:: _dll .. py:method:: call(name: str, *args: Any) -> None Call the D2XX function *name*; anything but ``FT_OK`` raises. :raises OSError: The driver answered an error, named in the message. .. py:method:: device_count() -> int Build the driver's device list and return its length. .. py:method:: device_info(index: int) -> tuple[str, str] Return the serial number and description of listed device *index*. .. py:method:: open_by_serial(serial: str) -> _D2xxHandle Open the device with serial number *serial*. The driver's device list is rebuilt first, as the ftd2xx package did by default: a device plugged in (or released by Kinesis) since the last listing is then found. .. py:function:: load_driver() -> _D2xxLibrary | None Return FTDI's D2XX library, or ``None`` when it is not installed here. Loaded on first use: the library exists only where FTDI's driver is installed, and a missing one must read as "no D2XX" rather than stop the caller from importing. .. py:function:: list_devices() -> list[D2xxDevice] Return every controller the D2XX driver lists. Opens nothing. The driver builds this list from the USB descriptors alone, which is why a controller Kinesis holds still appears in it. .. py:class:: D2xxPort(serial: str) Serial-like adapter over one D2XX device, for ``AptTrafficManager``. Provides what the traffic manager calls on its port: ``write``, ``read``, ``in_waiting``, ``is_open``, ``close``, ``reset_input_buffer`` and ``reset_output_buffer``. Remember which controller to open; :meth:`open` opens it. :param serial: The controller's serial number, as the driver lists it. .. py:attribute:: serial .. py:attribute:: _dev :type: Any | None :value: None .. py:method:: open() -> None Open the device and set up the APT link: 115200 8N1, RTS/CTS. :raises RuntimeError: FTDI's D2XX library is not installed on this host. :raises OSError: The device would not open, typically because Kinesis (or another process) holds it. .. py:property:: is_open :type: bool Return ``True`` while the device is open. .. py:property:: in_waiting :type: int Number of received bytes waiting to be read. .. py:method:: write(data: bytes) -> int Send *data* to the controller and return the byte count. .. py:method:: read(size: int) -> bytes Return up to *size* received bytes. .. py:method:: reset_input_buffer() -> None Discard received bytes not yet read. .. py:method:: reset_output_buffer() -> None Discard bytes queued for sending. .. py:method:: close() -> None Release the device so Kinesis (or the next session) can open it.