diff --git a/canopen/network.py b/canopen/network.py index 00c21cf0..075fcdec 100644 --- a/canopen/network.py +++ b/canopen/network.py @@ -140,6 +140,8 @@ def add_node( node: Union[int, RemoteNode, LocalNode], object_dictionary: Union[str, ObjectDictionary, None] = None, upload_eds: bool = False, + block_transfer: bool = False, + eds_format_handler : Callable = None ) -> RemoteNode: """Add a remote node to the network. @@ -152,6 +154,12 @@ def add_node( :class:`canopen.ObjectDictionary` object. :param upload_eds: Set ``True`` if EDS file should be uploaded from 0x1021. + :param block_transfer: + Set ``True`` if EDS file should be uploaded using block transfer mechanism + This can increase speed if supported + :param eds_format_handler: + Handler for generating .eds in case a custom format is used (object 0x1022) + This is manufacturer specific and can be used to extract a compressed EDS :return: The Node object that was added. @@ -159,7 +167,7 @@ def add_node( if isinstance(node, int): if upload_eds: logger.info("Trying to read EDS from node %d", node) - object_dictionary = import_from_node(node, self) + object_dictionary = import_from_node(node,self,block_transfer,eds_format_handler) node = RemoteNode(node, object_dictionary) self[node.id] = node return node diff --git a/canopen/objectdictionary/eds.py b/canopen/objectdictionary/eds.py index 872df234..269cfa48 100644 --- a/canopen/objectdictionary/eds.py +++ b/canopen/objectdictionary/eds.py @@ -7,7 +7,7 @@ except ImportError: from ConfigParser import RawConfigParser, NoOptionError, NoSectionError from canopen import objectdictionary -from canopen.sdo import SdoClient +from canopen.sdo import SdoClient, SdoAbortedError logger = logging.getLogger(__name__) @@ -167,21 +167,39 @@ def import_eds(source, node_id): return od -def import_from_node(node_id, network): +def import_from_node(node_id, network,block_transfer =False,eds_format_handler = None): """ Download the configuration from the remote node :param int node_id: Identifier of the node :param network: network object + :param block_transfer: use block transfer + :param eds_format_handler: Callable with custom logic that should return the EDS """ # Create temporary SDO client sdo_client = SdoClient(0x600 + node_id, 0x580 + node_id, objectdictionary.ObjectDictionary()) sdo_client.network = network # Subscribe to SDO responses network.subscribe(0x580 + node_id, sdo_client.on_response) + # Get Storage format (0x1022) + try: + storage_format = int.from_bytes(sdo_client.upload(0x1022,0),byteorder="little") + except SdoAbortedError as e: + # Default to 0 if not present + storage_format = 0 + logger.debug("Failed to read object 0x1022, for EDS storage format, default to ASCII (0)") # Create file like object for Store EDS variable try: - eds_fp = sdo_client.open(0x1021, 0, "rt") - od = import_eds(eds_fp, node_id) + if eds_format_handler is not None: + eds_raw_fp = sdo_client.open(0x1021, 0, "rb",block_transfer=block_transfer) + # Custom format handler must return a fp or string to extracted EDS file + eds_fp = eds_format_handler(eds_raw_fp,storage_format) + else: + if storage_format != 0: + logger.warning("Trying to read EDS with ASCII format (0) even though storage format is not 0") + eds_fp = sdo_client.open(0x1021, 0, "rt", block_transfer=block_transfer) + od = import_eds(eds_fp,node_id) + except Exception as e: + print(e) logger.error("No object dictionary could be loaded for node %d: %s", node_id, e) od = None diff --git a/doc/network.rst b/doc/network.rst index 4fad9cd4..fffd562d 100644 --- a/doc/network.rst +++ b/doc/network.rst @@ -38,6 +38,31 @@ Add nodes to the network using the :meth:`~canopen.Network.add_node` method:: local_node = canopen.LocalNode(1, '/path/to/master_dictionary.eds') network.add_node(local_node) +Some nodes store their EDS, using object 0x1021.The node can be added using +it's internal EDS in the following way:: + + # Add node and import EDS from node + node = network.add_node(6,upload_eds=True) + + # Optional block_transfer enabled + node = network.add_node(6,upload_eds = True, block_transfer = True) + + # Optionally add a handler if the file uses a specific format in order to + # create an EDS. This is an example with a zip compressed EDS. + import zipfile + from io import BytesIO, TextIOWrapper + + def unzip_eds_handler(fp,storage_format): + zip = zipfile.ZipFile(BytesIO(fp.read())) + io = TextIOWrapper(zip.open(name="device.eds"),encoding="ascii") + return io + + node = network.add_node(6, + upload_eds = True, + block_transfer = True, + eds_format_handler = unzip_eds_handler) + + Nodes can also be accessed using the ``Network`` object as a Python dictionary:: for node_id in network: