diff --git a/doc/source/index.rst b/doc/source/index.rst index 94eb43b..3b0cd75 100755 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -17,7 +17,7 @@ It also contains a wrapper for a simplified usage of the `Linux SocketCAN IsoTP .. note:: You are looking at the isotp v2.x documentation. The legacy `v1.x documentation `_ is still online. -V2.x changes +v2.x changes ------------ V2.x addressed several flaws that were present in v1.x. The main change is regarding the timing capabilities of the module. V2.x can achieve much better timing performance than @@ -34,6 +34,16 @@ Here is the major API changes to v2.x that might make an application designed wi - The transport layer can perform blocking sends, allowing an UDS layer to better handle its timeouts (P2/P2* vs P6 timeouts) - Some methods dedicated to internal usage have been prefixed with an underscore (``_``) to indicates that they are internals - - The CanStack object uses a Notifier instead of performing ``bus.recv()`` solving the popular issue of a CanStack depleting the receive queue and starving other modules from their incoming messages - The ``isotp.socket.recv()`` method does not return ``None`` on timeout anymore. The API now comply with the Python socket API and will raise the proper exception in case of timeout. + - The error handler is called from a different thread + - The :class:`TransportLayer` object is now an extension of the legacy v1.x TransportLayer, which has been renamed to ``TransportLayerLogic``. See :ref:`Backward Compatibility` and :ref:`Legacy Methods` + +On top of that, some improvement makes v2.x preferable over v1.x + + - The :class:`NotifierBasedCanStack` object has been introduced and uses a notifier instead of calling ``bus.recv()``, solving the popular issue of a CanStack depleting the receive queue and starving other modules from their incoming messages + - :ref:`Asymmetric addressing` is possible (different address for reception than transmission) + - Sending data with a generator is now possible, accommodating use cases with large payloads + - The module is fully type-hinted + - It is possible to use a busy-wait to achieve even more precise timings. See the :ref:`wait_func parameter` + diff --git a/doc/source/isotp/addressing.rst b/doc/source/isotp/addressing.rst index 1522a56..2941cdf 100755 --- a/doc/source/isotp/addressing.rst +++ b/doc/source/isotp/addressing.rst @@ -161,6 +161,7 @@ Example : ------ +.. _asymmetric_addresses: Asymmetric addresses -------------------- @@ -170,7 +171,7 @@ It is possible to send and receive with different address schemes. The :class:`A .. autoclass:: isotp.AsymmetricAddress When using an asymmetric, both ``tx_addr`` and ``rx_addr`` must be partial addresses, meaning that either ``tx_only=True`` or ``rx_only=True`` is set. -Address object instantiated with ``rx_only=True`` will not expect parameter meant for transmission and conversely, when instantiated with ``tx_only=True`` +Address objects instantiated with ``rx_only=True`` will not expect the parameters meant for transmission and conversely, when instantiated with ``tx_only=True`` parameters required for reception won't be needed. @@ -203,7 +204,7 @@ Example : 0x123 [7] 99 21 05 06 07 08 09 // Consecutive frame -The following table indicates the required parameter to construct a :class:`Address` object for all possible scenario +The following table indicates the required parameters to construct a :class:`Address` object for all possible scenarios .. csv-table:: :class:`Address` required parameters :header: "Addressing mode", "Full address", "Partial Tx (``tx_only=True``)", "Partial Rx (``rx_only=True``)" diff --git a/doc/source/isotp/implementation.rst b/doc/source/isotp/implementation.rst index c924739..de8e468 100755 --- a/doc/source/isotp/implementation.rst +++ b/doc/source/isotp/implementation.rst @@ -1,5 +1,5 @@ -Implementation -============== +Pure Python Implementation +========================== This sections explains the python implementation of the IsoTP protocol. @@ -14,8 +14,10 @@ In such case, the :class:`isotp.TransportLayer` will be th If python-can must be used as CAN layer, one can use the :class:`isotp.CanStack` and :class:`isotp.NotifierBasedCanStack` which extends the TransportLayer object with predefined functions that calls python-can. -.. autoclass:: isotp.CanStack .. autoclass:: isotp.NotifierBasedCanStack +.. autoclass:: isotp.CanStack + +.. note:: The :class:`CanStack` exists mainly for backward compatibility with v1.x. It is suggested to use the :class:`NotifierBasedCanStack` to avoid starvation issues and achieve better performances. The CAN messages going in and out from the transport layer are defined with :class:`isotp.CanMessage`. @@ -95,6 +97,7 @@ The transport layer ``params`` parameter must be a dictionary with the following .. _param_rx_flowcontrol_timeout: .. attribute:: rx_flowcontrol_timeout + :annotation: (int) **default: 1000** @@ -318,6 +321,8 @@ The :class:`isotp.TransportLayer` object has the following ----- +.. _legacy_methods: + Legacy methods (v1.x) --------------------- diff --git a/doc/source/isotp/socket.rst b/doc/source/isotp/socket.rst index acfb1bd..4e829d2 100755 --- a/doc/source/isotp/socket.rst +++ b/doc/source/isotp/socket.rst @@ -43,7 +43,7 @@ Without this project .. code-block:: python - SOL_CAN_ISOTP = 106 # These constants exist in the module header, not in Python. + SOL_CAN_ISOTP = 106 # These constants exist in the linux source code, not in Python CAN_ISOTP_RECV_FC = 2 # Many more exists. diff --git a/isotp/protocol.py b/isotp/protocol.py index 6ac9a11..9811c83 100755 --- a/isotp/protocol.py +++ b/isotp/protocol.py @@ -721,6 +721,13 @@ def recv(self, block: bool = False, timeout: Optional[float] = None) -> Optional """ Dequeue an IsoTP frame from the reception queue if available. + :param block: Tells if the read should be blocking or not + :type block: bool + + :param timeout: Timeout value used for blocking read only + :type timeout: float + + :return: The next available IsoTP frame :rtype: bytearray or None """ @@ -731,7 +738,7 @@ def recv(self, block: bool = False, timeout: Optional[float] = None) -> Optional def available(self) -> bool: """ - Returns ``True`` if an IsoTP frame is awaiting in the reception ``queue``. ``False`` otherwise + Returns ``True`` if an IsoTP frame is awaiting in the reception queue. ``False`` otherwise """ return not self.rx_queue.empty() @@ -1482,7 +1489,7 @@ def _read_relay_queue(self, timeout: Optional[float]) -> Optional[CanMessage]: return None def start(self) -> None: - """Start the IsoTP layer. Starts internal threads that handle the IsoTP communication.""" + """Starts the IsoTP layer. Starts internal threads that handle the IsoTP communication.""" self.logger.debug(f"Starting {self.__class__.__name__}") if self.started: raise RuntimeError("Transport Layer is already started") @@ -1610,16 +1617,16 @@ def stop_receiving(self): # Protect against usage of non thread-safe methods while threads are running. We don't hide those method for backward compatibility @is_documented_by(TransportLayerLogic.process) - def process(self, *args, **kwargs): + def process(self, rx_timeout: float = 0.0, do_rx: bool = True, do_tx: bool = True) -> TransportLayerLogic.ProcessStats: if self.started: raise RuntimeError("Cannot call process() after a start(). See documentation and notes about backward compatibility.") - super().process(*args, **kwargs) + return super().process(rx_timeout=rx_timeout, do_rx=do_rx, do_tx=do_tx) @is_documented_by(TransportLayerLogic.reset) - def reset(self, *args, **kwargs): + def reset(self): if self.started: raise RuntimeError("Cannot call reset() after a start(). See documentation and notes about backward compatibility.") - super().reset(*args, **kwargs) + super().reset() class BusOwner: @@ -1656,7 +1663,7 @@ def _python_can_to_isotp_message(msg: Optional["can.Message"]) -> Optional[CanMe class CanStack(TransportLayer, BusOwner): """ - The IsoTP transport layer preconfigured to use `python-can `__ as CAN layer. python-can must be installed in order to use this class. + The IsoTP transport layer pre configured to use `python-can `__ as CAN layer. python-can must be installed in order to use this class. All parameters except the ``bus`` parameter will be given to the :class:`TransportLayer` constructor This class directly calls ``bus.recv``, consuming the message from the receive queue, potentially starving other application. Consider using the :class:`NotifierBasedCanStack` @@ -1696,7 +1703,7 @@ def set_bus(self, bus: "can.BusABC") -> None: class NotifierBasedCanStack(TransportLayer, BusOwner): """ - The IsoTP transport layer preconfigured to use `python-can `__ as CAN layer and reading through a ``can.Notifier``. python-can must be installed in order to use this class. + The IsoTP transport layer pre configured to use `python-can `__ as CAN layer and reading through a ``can.Notifier``. python-can must be installed in order to use this class. All parameters except the ``bus`` and the ``notifier`` parameter will be given to the :class:`TransportLayer` constructor This class reads by registering a listener to the given notifier and sends by calling ``bus.recv``.