Source code for ingenialink.ethercat.dictionary

from functools import cached_property
from typing import Optional
from xml.etree import ElementTree

import ingenialogger

from ingenialink.constants import (
    CANOPEN_ADDRESS_OFFSET,
    CANOPEN_SUBNODE_0_ADDRESS_OFFSET,
    MAP_ADDRESS_OFFSET,
)
from ingenialink.dictionary import DictionarySafetyModule, DictionaryV2, Interface
from ingenialink.enums.register import (
    RegAccess,
    RegAddressType,
    RegCyclicType,
    RegDtype,
)
from ingenialink.ethercat.register import EthercatRegister

logger = ingenialogger.get_logger(__name__)


[docs] class EthercatDictionaryV2(DictionaryV2): """Contains all registers and information of a EtherCAT dictionary. Args: dictionary_path: Path to the Ingenia dictionary. """ interface = Interface.ECAT @cached_property def _monitoring_disturbance_registers(self) -> list[EthercatRegister]: return [ EthercatRegister( identifier="MON_DATA_VALUE", units="", subnode=0, idx=0x58B2, subidx=0x01, cyclic=RegCyclicType.CONFIG, dtype=RegDtype.BYTE_ARRAY_512, access=RegAccess.RO, ), EthercatRegister( identifier="DIST_DATA_VALUE", units="", subnode=0, idx=0x58B4, subidx=0x01, cyclic=RegCyclicType.CONFIG, dtype=RegDtype.BYTE_ARRAY_512, access=RegAccess.WO, ), ] @cached_property def _safety_registers(self) -> list[EthercatRegister]: return [ EthercatRegister( identifier="FSOE_TOTAL_ERROR", idx=0x4193, subidx=0x00, dtype=RegDtype.U16, access=RegAccess.RW, subnode=0, ), EthercatRegister( identifier="MDP_CONFIGURED_MODULE_1", idx=0xF030, subidx=0x01, dtype=RegDtype.U32, access=RegAccess.RW, subnode=0, ), EthercatRegister( identifier="FSOE_SAFE_INPUTS_MAP", idx=0x46D2, subidx=0x00, dtype=RegDtype.U16, access=RegAccess.RW, subnode=0, ), EthercatRegister( identifier="FSOE_SS1_TIME_TO_STO_1", idx=0x6651, subidx=0x01, dtype=RegDtype.U16, access=RegAccess.RW, subnode=0, ), ] @cached_property def _safety_modules(self) -> list[DictionarySafetyModule]: def __module_ident(module_idx: int) -> int: if self.product_code is None: raise ValueError("Module ident cannot be calculated, product code missing.") return (self.product_code & 0x7F00000) + module_idx return [ DictionarySafetyModule( uses_sra=False, module_ident=__module_ident(0), application_parameters=[ DictionarySafetyModule.ApplicationParameter(uid="FSOE_SAFE_INPUTS_MAP"), DictionarySafetyModule.ApplicationParameter(uid="FSOE_SS1_TIME_TO_STO_1"), ], ), DictionarySafetyModule( uses_sra=True, module_ident=__module_ident(1), application_parameters=[ DictionarySafetyModule.ApplicationParameter(uid="FSOE_SAFE_INPUTS_MAP"), DictionarySafetyModule.ApplicationParameter(uid="FSOE_SS1_TIME_TO_STO_1"), ], ), ] @cached_property def __pdo_registers(self) -> list[EthercatRegister]: return [ EthercatRegister( identifier="ETG_COMMS_RPDO_ASSIGN_TOTAL", units="", subnode=0, idx=0x1C12, subidx=0x00, dtype=RegDtype.S32, access=RegAccess.RW, address_type=RegAddressType.NVM_NONE, ), EthercatRegister( identifier="ETG_COMMS_RPDO_ASSIGN_1", units="", subnode=0, idx=0x1C12, subidx=0x01, dtype=RegDtype.S32, access=RegAccess.RW, address_type=RegAddressType.NVM_NONE, ), EthercatRegister( identifier="ETG_COMMS_RPDO_MAP1_TOTAL", units="", subnode=0, idx=0x1600, subidx=0x00, dtype=RegDtype.S32, access=RegAccess.RW, address_type=RegAddressType.NVM_NONE, ), EthercatRegister( identifier="ETG_COMMS_RPDO_MAP1_1", units="", subnode=0, idx=0x1600, subidx=0x01, dtype=RegDtype.STR, access=RegAccess.RW, address_type=RegAddressType.NVM_NONE, ), EthercatRegister( identifier="ETG_COMMS_TPDO_ASSIGN_TOTAL", units="", subnode=0, idx=0x1C13, subidx=0x00, dtype=RegDtype.S32, access=RegAccess.RW, address_type=RegAddressType.NVM_NONE, ), EthercatRegister( identifier="ETG_COMMS_TPDO_ASSIGN_1", units="", subnode=0, idx=0x1C13, subidx=0x01, dtype=RegDtype.S32, access=RegAccess.RW, address_type=RegAddressType.NVM_NONE, ), EthercatRegister( identifier="ETG_COMMS_TPDO_MAP1_TOTAL", units="", subnode=0, idx=0x1A00, subidx=0x00, dtype=RegDtype.S32, access=RegAccess.RW, address_type=RegAddressType.NVM_NONE, ), EthercatRegister( identifier="ETG_COMMS_TPDO_MAP1_1", units="", subnode=0, idx=0x1A00, subidx=0x01, dtype=RegDtype.STR, access=RegAccess.RW, address_type=RegAddressType.NVM_NONE, ), ] @staticmethod def __get_cia_offset(subnode: int) -> int: """Get the CiA offset for the register based on the subnode. Args: subnode: register subnode. Returns: The CiA offset for the register. """ return ( CANOPEN_SUBNODE_0_ADDRESS_OFFSET if subnode == 0 else CANOPEN_ADDRESS_OFFSET + MAP_ADDRESS_OFFSET * (subnode - 1) ) def _read_xdf_register(self, register: ElementTree.Element) -> Optional[EthercatRegister]: current_read_register = super()._read_xdf_register(register) if current_read_register is None: return None try: idx = int(register.attrib["address"], 16) + self.__get_cia_offset( current_read_register.subnode ) subidx = 0x00 ethercat_register = EthercatRegister( idx, subidx, current_read_register.dtype, current_read_register.access, identifier=current_read_register.identifier, units=current_read_register.units, cyclic=current_read_register.cyclic, phy=current_read_register.phy, subnode=current_read_register.subnode, storage=current_read_register.storage, reg_range=current_read_register.range, labels=current_read_register.labels, enums=current_read_register.enums, cat_id=current_read_register.cat_id, scat_id=current_read_register.scat_id, internal_use=current_read_register.internal_use, address_type=current_read_register.address_type, bitfields=current_read_register.bitfields, ) return ethercat_register except KeyError as ke: logger.error( f"Register with ID {current_read_register.identifier} has not attribute {ke}" ) return None def _append_missing_registers( self, ) -> None: """Append missing registers to the dictionary. Mainly registers needed for Monitoring/Disturbance and PDOs. """ super()._append_missing_registers() for register in self.__pdo_registers: if register.identifier is not None: self._registers[register.subnode][register.identifier] = register