Skip to content

Commit

Permalink
Doc update (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
pylessard authored Jan 4, 2024
1 parent c851cd1 commit f517322
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 16 deletions.
14 changes: 12 additions & 2 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://can-isotp.readthedocs.io/en/v1.x>`_ 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
Expand All @@ -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<isotp.TransportLayer>` object is now an extension of the legacy v1.x TransportLayer, which has been renamed to ``TransportLayerLogic``. See :ref:`Backward Compatibility<backward_compatibility>` and :ref:`Legacy Methods<legacy_methods>`

On top of that, some improvement makes v2.x preferable over v1.x

- The :class:`NotifierBasedCanStack<isotp.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<asymmetric_addresses>` 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<param_wait_func>`

5 changes: 3 additions & 2 deletions doc/source/isotp/addressing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ Example :

------

.. _asymmetric_addresses:

Asymmetric addresses
--------------------
Expand All @@ -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.


Expand Down Expand Up @@ -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<isotp.Address>` object for all possible scenario
The following table indicates the required parameters to construct a :class:`Address<isotp.Address>` object for all possible scenarios

.. csv-table:: :class:`Address<isotp.Address>` required parameters
:header: "Addressing mode", "Full address", "Partial Tx (``tx_only=True``)", "Partial Rx (``rx_only=True``)"
Expand Down
11 changes: 8 additions & 3 deletions doc/source/isotp/implementation.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Implementation
==============
Pure Python Implementation
==========================

This sections explains the python implementation of the IsoTP protocol.

Expand All @@ -14,8 +14,10 @@ In such case, the :class:`isotp.TransportLayer<isotp.TransportLayer>` will be th

If python-can must be used as CAN layer, one can use the :class:`isotp.CanStack<isotp.CanStack>` and :class:`isotp.NotifierBasedCanStack<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<isotp.CanStack>` exists mainly for backward compatibility with v1.x. It is suggested to use the :class:`NotifierBasedCanStack<isotp.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<isotp.CanMessage>`.

Expand Down Expand Up @@ -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**

Expand Down Expand Up @@ -318,6 +321,8 @@ The :class:`isotp.TransportLayer<isotp.TransportLayer>` object has the following

-----

.. _legacy_methods:

Legacy methods (v1.x)
---------------------

Expand Down
2 changes: 1 addition & 1 deletion doc/source/isotp/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
23 changes: 15 additions & 8 deletions isotp/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
Expand All @@ -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()

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 <https://python-can.readthedocs.io>`__ as CAN layer. python-can must be installed in order to use this class.
The IsoTP transport layer pre configured to use `python-can <https://python-can.readthedocs.io>`__ 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<isotp.TransportLayer>` constructor
This class directly calls ``bus.recv``, consuming the message from the receive queue, potentially starving other application. Consider using the :class:`NotifierBasedCanStack<isotp.NotifierBasedCanStack>`
Expand Down Expand Up @@ -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 <https://python-can.readthedocs.io>`__ 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 <https://python-can.readthedocs.io>`__ 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<isotp.TransportLayer>` constructor
This class reads by registering a listener to the given notifier and sends by calling ``bus.recv``.
Expand Down

0 comments on commit f517322

Please sign in to comment.