Source code for ingenialink.register

from abc import ABC
from typing import Any, Optional, Union

from ingenialink import exceptions as exc
from ingenialink.bitfield import BitField
from ingenialink.enums.register import (
    RegAccess,
    RegAddressType,
    RegCyclicType,
    RegDtype,
    RegPhy,
)
from ingenialink.utils._utils import convert_bytes_to_dtype

dtypes_ranges: dict[RegDtype, dict[str, Union[int, float]]] = {
    RegDtype.U8: {"max": 255, "min": 0},
    RegDtype.S8: {"max": 127, "min": -128},
    RegDtype.U16: {"max": 65535, "min": 0},
    RegDtype.S16: {"max": 32767, "min": -32767 - 1},
    RegDtype.U32: {"max": 4294967295, "min": 0},
    RegDtype.S32: {"max": 2147483647, "min": -2147483647 - 1},
    RegDtype.U64: {"max": 18446744073709551615, "min": 0},
    RegDtype.S64: {"max": 9223372036854775807, "min": 9223372036854775807 - 1},
    RegDtype.FLOAT: {"max": 3.4e38, "min": -3.4e38},
}


[docs] class Register(ABC): """Register Base class. Args: dtype: Data type. access: Access type. identifier: Identifier. units: Units. cyclic: Cyclic typed register. phy: Physical units. subnode: Subnode. storage: Storage. reg_range: Range (min, max). labels: Register labels. enums: Enumeration registers. cat_id: Category ID. scat_id: Sub-category ID. internal_use: Internal use. address_type: Address tpye. description: Register description. default: Register default value. bitfields: Fields that specify groups of bits Raises: TypeError: If any of the parameters has invalid type. ILValueError: If the register is invalid. ILAccessError: Register with wrong access type. """ def __init__( self, dtype: RegDtype, access: RegAccess, identifier: Optional[str] = None, units: Optional[str] = None, cyclic: RegCyclicType = RegCyclicType.CONFIG, phy: RegPhy = RegPhy.NONE, subnode: int = 1, storage: Any = None, reg_range: Union[ tuple[None, None], tuple[int, int], tuple[float, float], tuple[str, str] ] = (None, None), labels: Optional[dict[str, str]] = None, enums: Optional[dict[str, int]] = None, cat_id: Optional[str] = None, scat_id: Optional[str] = None, internal_use: int = 0, address_type: Optional[RegAddressType] = None, description: Optional[str] = None, default: Optional[bytes] = None, bitfields: Optional[dict[str, BitField]] = None, ) -> None: if labels is None: labels = {} if enums is None: enums = {} self.__type_errors(dtype, access, phy) self._dtype = dtype.value self._access = access.value self._identifier = identifier self._units = units self._cyclic = cyclic self._phy = phy.value self._subnode = subnode self._storage = storage self._range = reg_range if reg_range else (None, None) self._labels = labels self._cat_id = cat_id self._scat_id = scat_id self._internal_use = internal_use self._storage_valid = storage is not None self._address_type = address_type self._description = description self._default = default self._enums = enums self.__bitfields = bitfields self.__config_range(reg_range) def __type_errors(self, dtype: RegDtype, access: RegAccess, phy: RegPhy) -> None: if not isinstance(dtype, RegDtype): raise exc.ILValueError("Invalid data type") if not isinstance(access, RegAccess): raise exc.ILAccessError("Invalid access type") if not isinstance(phy, RegPhy): raise exc.ILValueError("Invalid physical units type") def __config_range( self, reg_range: Union[tuple[None, None], tuple[int, int], tuple[float, float], tuple[str, str]], ) -> None: cast_type: Union[type[int], type[float]] if self.dtype not in dtypes_ranges: self._storage_valid = False return elif self.dtype == RegDtype.FLOAT: cast_type = float else: cast_type = int reg_range_min = ( cast_type(reg_range[0]) if reg_range[0] is not None else dtypes_ranges[self.dtype]["min"] ) reg_range_max = ( cast_type(reg_range[1]) if reg_range[1] is not None else dtypes_ranges[self.dtype]["max"] ) self._range = reg_range_min, reg_range_max if self.storage is not None: self._storage = cast_type(self.storage) @property def dtype(self) -> RegDtype: """Data type of the register.""" return RegDtype(self._dtype) @property def access(self) -> RegAccess: """Access type of the register.""" return RegAccess(self._access) @property def identifier(self) -> Optional[str]: """Register identifier.""" return self._identifier @property def units(self) -> Optional[str]: """Units of the register.""" return self._units @property def cyclic(self) -> RegCyclicType: """Defines if the register is cyclic.""" return self._cyclic @property def phy(self) -> RegPhy: """Physical units of the register.""" return RegPhy(self._phy) @property def subnode(self) -> int: """Target subnode of the register.""" return self._subnode @property def storage(self) -> Any: """Defines if the register needs to be stored.""" if not self.storage_valid: return None if self.dtype in [ RegDtype.S8, RegDtype.U8, RegDtype.S16, RegDtype.U16, RegDtype.S32, RegDtype.U32, RegDtype.S64, RegDtype.U64, RegDtype.FLOAT, ]: return self._storage else: return None @storage.setter def storage(self, value: Any) -> None: """Defines if the register needs to be stored.""" self._storage = value @property def storage_valid(self) -> bool: """Defines if the register storage is valid.""" return self._storage_valid @storage_valid.setter def storage_valid(self, value: bool) -> None: """Defines if the register storage is valid.""" self._storage_valid = value @property def range( self, ) -> Union[tuple[None, None], tuple[int, int], tuple[float, float], tuple[str, str]]: """tuple: Containing the minimum and the maximum values of the register.""" if self._range: return self._range return (None, None) @property def labels(self) -> dict[str, str]: """Containing the labels of the register.""" return self._labels @property def enums(self) -> dict[str, int]: """Containing all the enums for the register.""" return self._enums @property def enums_count(self) -> int: """The number of the enums in the register.""" return len(self._enums) @property def cat_id(self) -> Optional[str]: """Category ID.""" return self._cat_id @property def scat_id(self) -> Optional[str]: """Sub-Category ID.""" return self._scat_id @property def internal_use(self) -> int: """Defines if the register is only for internal uses.""" return self._internal_use @property def address_type(self) -> Optional[RegAddressType]: """Address type of the register.""" return RegAddressType(self._address_type) @property def description(self) -> Optional[str]: """Register description.""" return self._description @property def default(self) -> Union[None, int, float, str, bytes]: """Register default value.""" if self._default is None: return self._default return convert_bytes_to_dtype(self._default, self.dtype) @property def mapped_address(self) -> int: """Register mapped address used for monitoring/disturbance.""" raise NotImplementedError @property def bitfields(self) -> Optional[dict[str, BitField]]: """Register bit fields.""" return self.__bitfields