Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial bit masks #75

Merged
merged 7 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions roboglia/base/devices/DUMMY.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,25 +109,32 @@ registers:
access: RW
class: BoolRegister


status_one:
address: 99
clone: True
class: BoolRegister
mask: 0b00000001

bits: 0b00000001

status_2and3:
address: 99
clone: True
access: RW
class: BoolRegister
mask: 0b00000110
bits: 0b00000110
mode: all

status_2or3:
address: 99
clone: True
class: BoolRegister
mask: 0b00000110
bits: 0b00000110
mode: any

status_masked:
address: 99
clone: True
access: RW
class: BoolRegister
bits: 0b10100000
mask: 0b11110000
mode: all
68 changes: 46 additions & 22 deletions roboglia/base/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,54 +389,78 @@ class BoolRegister(BaseRegister):

Parameters
----------
mask: int or ``None``
An optional mask to use in the determination of the output of the
register. Default is None and in this case we simply compare the
bits: int or ``None``
An optional bit pattern to use in the determination of the output of
the register. Default is None and in this case we simply compare the
internal value with 0.

mode: str ('all' or 'any')
Indicates how the mask should be used: 'all' means all the bits
in the mask must match while 'any'
means any bit that matches the mask is enough to result in a ``True``
external value. Only used if mask is not ``None``. Default is 'any'.
Indicates how the bit pattern should be used: 'all' means all the bits
in the pattern must match while 'any'
means any bit that matches the pattern is enough to result in a
``True`` external value. Only used if bits is not ``None``. Default
is 'any'.

mask: int or ``None``
An optional maks that allows for partial bit handling on the
internal values. This mask permits handling only the specified bits
without affecting the other ones in the internal value. For instance
if the mask is 0b00001111 then the operations (setter, getter) will
only affect the most significant 4 bits of the register.
"""
def __init__(self, mask=None, mode='any', **kwargs):
def __init__(self, bits=None, mode='any', mask=None, **kwargs):
super().__init__(**kwargs)
if mask:
check_type(mask, int, 'register', self.name, logger)
if bits:
check_type(bits, int, 'register', self.name, logger)
check_options(mode, ['all', 'any'], 'register', self.name, logger)
self.__mask = mask
if mask:
check_type(mask, int, 'register', self.name, logger)
self.__bits = bits
self.__mode = mode
self.__mask = mask

@property
def mask(self):
"""The mask used."""
return self.__mask
def bits(self):
"""The bit pattern used."""
return self.__bits

@property
def mode(self):
"""The bitmasking mode ('all' or 'any')."""
return self.__mode

@property
def mask(self):
"""The partial bitmask for the handling of the bits."""
return self.__mask

def value_to_external(self, value):
"""The external representation of bool register.
"""
if self.mask is None:
if self.bits is None:
return bool(value)
# this assumes that if a mask is used the bits in the ``bits``
# attribute are all 0 already and we don't need to AND the ``mask``
# with the bits
if self.mode == 'any':
return bool(value & self.mask)
return bool(value & self.bits)
if self.mode == 'all':
return (value & self.mask) == self.mask
return (value & self.bits) == self.bits
raise NotImplementedError

def value_to_internal(self, value):
"""The internal representation of the register's value.
"""
if not value:
return 0
if self.mask:
return self.mask
return 1
if not self.mask:
if not value:
return 0
if self.bits:
return self.bits
return 1
# else: not really needed
# the int() below is to remove a linter error
masked_int_value = self.int_value & (~ int(self.mask))
return self.bits | masked_int_value


class RegisterWithConversion(BaseRegister):
Expand Down
34 changes: 9 additions & 25 deletions roboglia/base/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Sensor():
to a register in that device that represents the value the sensor is
representing. In addition a sensor can have an optional register used
to activate or deactivate the device and can publish a ``value`` that
can be either boolean if the ``mask`` parameter is used or float, in
can be either boolean if the ``bits`` parameter is used or float, in
which case the sensor can also apply an ``inverse`` and and ``offset``
to the values read from the device registry.

Expand All @@ -53,27 +53,20 @@ class Sensor():
Indicates if the value read from the register should be inverted
before being presented to the user in the :py:meth:`value`. The
inverse operation is performed before the ``offset`` (see below).
Default is ``False``. It is ignored if ``mask`` property is used.
Default is ``False``. It is ignored if ``bits`` property is used.

offset: float
Indicates an offest to be adder to the value read from the register
(after ``inverse`` if ``True``). Default is 0.0. It is ignored if
``mask`` property is used.

mask: int
A bit mask used to AND the value read from device. After the AND is
performed the :py:meth:`value` will return ``True`` or ``False`` if
the result is different than 0 or not. If ``mask`` is used, the
paramters ``inverse`` and ``offset`` are ignored. If no mask is
needed, use ``None``. This is also the default.
``bits`` property is used.

auto: bool
Indicates if the sensor should be automatically activated when the
robot is started (:py:meth:roboglia.base.BaseRobot.`start` method).
Default is ``True``.
"""
def __init__(self, name='SENSOR', device=None, value_read=None,
activate=None, inverse=False, offset=0.0, mask=None,
activate=None, inverse=False, offset=0.0,
auto=True, **kwargs):
self.__name = name
check_not_empty(device, 'device', 'sensor', self.name, logger)
Expand All @@ -95,7 +88,6 @@ def __init__(self, name='SENSOR', device=None, value_read=None,
self.__inverse = inverse
check_type(offset, float, 'sensor', self.name, logger)
self.__offset = offset
self.__mask = mask
check_options(auto, [True, False], 'joint', self.name, logger)
self.__auto_activate = auto

Expand Down Expand Up @@ -165,29 +157,21 @@ def offset(self):
"""(read-only) The offset between sensor coords and device coords."""
return self.__offset

@property
def mask(self):
"""The (optional) bit mask to interpret the sensor data."""
return self.__mask

@property
def value(self):
"""Returns the value of the sensor.

Returns
-------
bool or float:
If ``mask`` is used the sensor is assumed to produce a ``float``
response resulted from the AND between the value of the register
in the device and the mask. If not mask is used the sensor is
assumed to produce a float value that is adjusted with the
The value of the register is adjusted with the
``offset`` and the ``inverse`` attributes.
"""
reg_value = self.read_register.value
if self.mask:
# boolean value
return (reg_value & self.mask) != 0
# float value
if isinstance(reg_value, bool):
# bool return
return ~ reg_value if self.inverse else reg_value
# float return
if self.inverse:
reg_value = - reg_value
return reg_value + self.offset
Expand Down
28 changes: 14 additions & 14 deletions roboglia/dynamixel/devices/AX-12A.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,49 +140,49 @@ registers:
clone: True
class: BoolRegister
access: RW
mask: 0b01000000
bits: 0b01000000

alarm_overload_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00100000
bits: 0b00100000

alarm_checksum_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00010000
bits: 0b00010000

alarm_range_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00001000
bits: 0b00001000

alarm_overheating_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00000100
bits: 0b00000100

alarm_anglelimit_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00000010
bits: 0b00000010

alarm_inputvoltage_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00000001
bits: 0b00000001

shutdown:
address: 18
Expand All @@ -194,49 +194,49 @@ registers:
clone: True
class: BoolRegister
access: RW
mask: 0b01000000
bits: 0b01000000

shutdown_overload_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00100000
bits: 0b00100000

shutdown_checksum_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00010000
bits: 0b00010000

shutdown_range_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00001000
bits: 0b00001000

shutdown_overheating_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00000100
bits: 0b00000100

shutdown_anglelimit_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00000010
bits: 0b00000010

shutdown_inputvoltage_error:
address: 17
clone: True
class: BoolRegister
access: RW
mask: 0b00000001
bits: 0b00000001

torque_enable:
class: BoolRegister
Expand Down
Loading