Source code for ingenialink.registers

from enum import Enum

from ._ingenialink import ffi, lib
from ._utils import cstr, pstr, INT_SIZES

from .dict_labels import LabelsDictionary

# CANOPEN DTYPES
IL_REG_DTYPE_DOMAIN = 15


[docs]class REG_DTYPE(Enum): """ Data Type. """ U8 = lib.IL_REG_DTYPE_U8 """ Unsigned 8-bit integer. """ S8 = lib.IL_REG_DTYPE_S8 """ Signed 8-bit integer. """ U16 = lib.IL_REG_DTYPE_U16 """ Unsigned 16-bit integer. """ S16 = lib.IL_REG_DTYPE_S16 """ Signed 16-bit integer. """ U32 = lib.IL_REG_DTYPE_U32 """ Unsigned 32-bit integer. """ S32 = lib.IL_REG_DTYPE_S32 """ Signed 32-bit integer. """ U64 = lib.IL_REG_DTYPE_U64 """ Unsigned 64-bit integer. """ S64 = lib.IL_REG_DTYPE_S64 """ Signed 64-bit integer. """ FLOAT = lib.IL_REG_DTYPE_FLOAT """ Float. """ STR = lib.IL_REG_DTYPE_STR """ String. """ DOMAIN = IL_REG_DTYPE_DOMAIN """ Domain. """
[docs]class REG_ACCESS(Enum): """ Access Type. """ RW = lib.IL_REG_ACCESS_RW """ Read/Write. """ RO = lib.IL_REG_ACCESS_RO """ Read-only. """ WO = lib.IL_REG_ACCESS_WO """ Write-only. """
[docs]class REG_PHY(Enum): """ Physical Units. """ NONE = lib.IL_REG_PHY_NONE """ None. """ TORQUE = lib.IL_REG_PHY_TORQUE """ Torque. """ POS = lib.IL_REG_PHY_POS """ Position. """ VEL = lib.IL_REG_PHY_VEL """ Velocity. """ ACC = lib.IL_REG_PHY_ACC """ Acceleration. """ VOLT_REL = lib.IL_REG_PHY_VOLT_REL """ Relative voltage (DC). """ RAD = lib.IL_REG_PHY_RAD """ Radians. """
def dtype_size(dtype): sizes = { REG_DTYPE.U8: 1, REG_DTYPE.S8: 1, REG_DTYPE.U16: 2, REG_DTYPE.S16: 2, REG_DTYPE.U32: 4, REG_DTYPE.S32: 4, REG_DTYPE.U64: 8, REG_DTYPE.S64: 8, REG_DTYPE.FLOAT: 4 } return sizes[dtype] def _get_reg_id(reg, subnode=1): """ Obtain Register and ID. Args: reg (str, Register): Register. """ if isinstance(reg, str): return ffi.NULL, cstr(reg) elif isinstance(reg, Register): return reg._reg, ffi.NULL raise TypeError('Unexpected register type')
[docs]class Register(object): """ Register. Args: identifier (str): Identifier. units (str): Units. address (int): Address. dtype (REG_DTYPE): Data type. access (REG_ACCESS): Access type. phy (REG_PHY, optional): Physical units. subnode (int): Subnode storage (any, optional): Storage. range (tuple, optional): Range (min, max). labels (dict, optional): Register labels. enums (dict, optional): Enumeration values. cat_id (str, optional): Category ID. scat_id (str, optional): Sub-category ID. internal_use (int, optional): Internal use. Raises: TypeError: If any of the parameters has invalid type. """ def __init__(self, identifier, units, cyclic, address, dtype, access, phy=REG_PHY.NONE, subnode=1, storage=None, range=None, labels={}, enums=[], enums_count=0, cat_id=None, scat_id=None, internal_use=0): if not isinstance(dtype, REG_DTYPE): raise TypeError('Invalid data type') if not isinstance(access, REG_ACCESS): raise TypeError('Invalid access type') if not isinstance(phy, REG_PHY): raise TypeError('Invalid physical units type') self._reg = ffi.new('il_reg_t *') # initialize register self._reg.identifier = ffi.new("char[]", cstr(identifier)) self._reg.units = ffi.new("char[]", cstr(units)) self._reg.address = address self._reg.subnode = subnode self._reg.cyclic = ffi.new("char[]", cstr(cyclic)) self._reg.dtype = dtype.value self._reg.access = access.value self._reg.phy = phy.value self._reg.internal_use = internal_use self._reg.storage_valid = 0 if not storage else 1 if dtype == REG_DTYPE.S8: if storage: self._reg.storage.s8 = int(storage) self._reg.range.min.s8 = (range[0] if range else INT_SIZES.S8_MIN.value) self._reg.range.max.s8 = (range[1] if range else INT_SIZES.S8_MAX.value) elif dtype == REG_DTYPE.U8: if storage: self._reg.storage.u8 = int(storage) self._reg.range.min.u8 = range[0] if range else 0 self._reg.range.max.u8 = (range[1] if range else INT_SIZES.U8_MAX.value) if dtype == REG_DTYPE.S16: if storage: self._reg.storage.s16 = int(storage) self._reg.range.min.s16 = (range[0] if range else INT_SIZES.S16_MIN.value) self._reg.range.max.s16 = (range[1] if range else INT_SIZES.S16_MAX.value) elif dtype == REG_DTYPE.U16: if storage: self._reg.storage.u16 = int(storage) self._reg.range.min.u16 = range[0] if range else 0 self._reg.range.max.u16 = (range[1] if range else INT_SIZES.U16_MAX.value) if dtype == REG_DTYPE.S32: if storage: self._reg.storage.s32 = int(storage) self._reg.range.min.s32 = (range[0] if range else INT_SIZES.S32_MIN.value) self._reg.range.max.s32 = (range[1] if range else INT_SIZES.S32_MAX.value) elif dtype == REG_DTYPE.U32: if storage: self._reg.storage.u32 = int(storage) self._reg.range.min.u32 = range[0] if range else 0 self._reg.range.max.u32 = (range[1] if range else INT_SIZES.U32_MAX.value) if dtype == REG_DTYPE.S64: if storage: self._reg.storage.s64 = int(storage) self._reg.range.min.s64 = (range[0] if range else INT_SIZES.S64_MIN.value) self._reg.range.max.s64 = (range[1] if range else INT_SIZES.S64_MAX.value) elif dtype == REG_DTYPE.U64: if storage: self._reg.storage.u64 = int(storage) self._reg.range.min.u64 = range[0] if range else 0 self._reg.range.max.u64 = (range[1] if range else INT_SIZES.U64_MAX.value) elif dtype == REG_DTYPE.FLOAT: if storage: self._reg.storage.flt = float(storage) self._reg.range.min.flt = (range[0] if range else -2147483648) self._reg.range.max.flt = (range[1] if range else 2147483647) else: self._reg.storage_valid = 0 self._labels = LabelsDictionary(labels) self._reg.labels = self._labels._labels self._reg.enums_count = enums_count self._reg.cat_id = ffi.NULL if not cat_id else cstr(cat_id) if not cat_id and scat_id: raise ValueError('Sub-category requires a parent category') self._reg.scat_id = ffi.NULL if not scat_id else cstr(scat_id) def __repr__(self): """ Obtain register object. Returns: str: Register information. """ # obtain category/subcategory information if self.cat_id: cat_info = self.cat_id if self.scat_id: cat_info += ' + ' + self.scat_id else: cat_info = 'Uncategorized' if self.storage and self.storage_valid: storage_info = self.storage else: storage_info = 'No storage' return '<Register: {}, {}, {}, {}, 0x{:08x}, {}{}, {}, {}, [], {},' \ 'ST: {}, [{}], {}>'.format( self.identifier, self.units, self.subnode, self.cyclic, self.address, self.dtype, ' ∊ ' + str(self.range) if self.range else '', self.access, self.phy, self.enums, self.enums_count, storage_info, cat_info, self.internal_use ) @classmethod def _from_register(cls, reg): """ Create a new class instance from an existing register. Args: reg (Register): Register instance. Returns: Register: Register object instance copied. """ inst = cls.__new__(cls) inst._reg = reg inst._labels = LabelsDictionary._from_labels(reg.labels) return inst @property def identifier(self): """ Obtain register identifier. Returns: str: Register identifier """ if self._reg.identifier != ffi.NULL: return pstr(self._reg.identifier) return None @property def units(self): """ Obtain register units. Returns: str: Register units """ if self._reg.units != ffi.NULL: return pstr(self._reg.units) @property def address(self): """ Obtain register address. Returns: int: Register address. """ return self._reg.address @property def subnode(self): """ Obtain register subnode. Returns: int: Register subnode. """ return self._reg.subnode @property def cyclic(self): """ Obtain register cyclic. Returns: str: Register cyclic type. """ if self._reg.cyclic != ffi.NULL: return pstr(self._reg.cyclic) @property def dtype(self): """ Obtain register dtype. Returns: int: Register data type. """ return REG_DTYPE(self._reg.dtype) @property def access(self): """ Obtain register access. Returns: int: Register access type. """ return REG_ACCESS(self._reg.access) @property def phy(self): """ Obtain register physical units. Returns: int: Register physical units. """ return REG_PHY(self._reg.phy) @property def storage(self): """ Obtain register storage. Returns: int: Register storage. """ if not self._reg.storage_valid: return None if self.dtype == REG_DTYPE.S8: return self._reg.storage.s8 elif self.dtype == REG_DTYPE.U8: return self._reg.storage.u8 if self.dtype == REG_DTYPE.S16: return self._reg.storage.s16 elif self.dtype == REG_DTYPE.U16: return self._reg.storage.u16 if self.dtype == REG_DTYPE.S32: return self._reg.storage.s32 elif self.dtype == REG_DTYPE.U32: return self._reg.storage.u32 if self.dtype == REG_DTYPE.S64: return self._reg.storage.s64 elif self.dtype == REG_DTYPE.U64: return self._reg.storage.u64 elif self.dtype == REG_DTYPE.FLOAT: return self._reg.storage.flt else: return None @property def range(self): """ Obtains register range. Returns: tuple: Register range (min, max), None if undefined. """ if self.dtype == REG_DTYPE.S8: return (self._reg.range.min.s8, self._reg.range.max.s8) elif self.dtype == REG_DTYPE.U8: return (self._reg.range.min.u8, self._reg.range.max.u8) if self.dtype == REG_DTYPE.S16: return (self._reg.range.min.s16, self._reg.range.max.s16) elif self.dtype == REG_DTYPE.U16: return (self._reg.range.min.u16, self._reg.range.max.u16) if self.dtype == REG_DTYPE.S32: return (self._reg.range.min.s32, self._reg.range.max.s32) elif self.dtype == REG_DTYPE.U32: return (self._reg.range.min.u32, self._reg.range.max.u32) if self.dtype == REG_DTYPE.S64: return (self._reg.range.min.s64, self._reg.range.max.s64) elif self.dtype == REG_DTYPE.U64: return (self._reg.range.min.u64, self._reg.range.max.u64) elif self.dtype == REG_DTYPE.FLOAT: return (self._reg.range.min.flt, self._reg.range.max.flt) return None @property def labels(self): """ Obtains register labels. Returns: LabelsDictionary: Labels dictionary. """ return self._labels @property def enums(self): """ Obtain enumerations list of the register. Returns: array: Enumerations of the register. """ if not hasattr(self, '_enums'): self._enums = [] for i in range(0, self._reg.enums_count): dict = { 'label': pstr(self._reg.enums[i].label), 'value': self._reg.enums[i].value } self._enums.append(dict) return self._enums @property def enums_count(self): """ Obtain number of enumerations of the register. Returns: int: Register Enumerations count. """ return self._reg.enums_count @property def cat_id(self): """ Category identifier. Returns: str | None: Current category identifier. """ if self._reg.cat_id != ffi.NULL: return pstr(self._reg.cat_id) return None @property def scat_id(self): """ Subcategory identifier. Returns: str | None: Current subcategory identifier. """ if self._reg.scat_id != ffi.NULL: return pstr(self._reg.scat_id) return None @property def internal_use(self): """ Internal use check. Returns: int: Register internal_use. """ return self._reg.internal_use