210 lines
6.5 KiB
Python
210 lines
6.5 KiB
Python
#
|
|
#
|
|
#
|
|
|
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
|
|
from weakref import proxy as _proxy
|
|
from copy import copy as _copy
|
|
|
|
from .common import NamedInt as _NamedInt, NamedInts as _NamedInts
|
|
|
|
#
|
|
#
|
|
#
|
|
|
|
KIND = _NamedInts(toggle=0x1, choice=0x02, range=0x12)
|
|
|
|
class _Setting(object):
|
|
__slots__ = ['name', 'label', 'description',
|
|
'kind', '_rw', '_validator',
|
|
'_device', '_value']
|
|
|
|
def __init__(self, name, rw, validator, kind=None, label=None, description=None):
|
|
assert name
|
|
self.name = name
|
|
self.label = label or name
|
|
self.description = description
|
|
|
|
self._rw = rw
|
|
self._validator = validator
|
|
|
|
assert kind is None or kind & validator.kind != 0
|
|
self.kind = kind or validator.kind
|
|
|
|
def __call__(self, device):
|
|
o = _copy(self)
|
|
o._value = None
|
|
o._device = _proxy(device)
|
|
return o
|
|
|
|
@property
|
|
def choices(self):
|
|
return self._validator.choices if self._validator.kind & KIND.choice else None
|
|
|
|
def read(self, cached=True):
|
|
if self._device:
|
|
if self._value is None or not cached:
|
|
reply = self._rw.read(self._device)
|
|
# print ("read reply", repr(reply))
|
|
if reply:
|
|
# print ("pre-read", self._value)
|
|
self._value = self._validator.validate_read(reply)
|
|
# print ("post-read", self._value)
|
|
return self._value
|
|
|
|
def write(self, value):
|
|
if self._device:
|
|
data_bytes = self._validator.prepare_write(value)
|
|
reply = self._rw.write(self._device, data_bytes)
|
|
if reply:
|
|
self._value = self._validator.validate_write(value, reply)
|
|
return self._value
|
|
|
|
def __str__(self):
|
|
if hasattr(self, '_value'):
|
|
assert hasattr(self, '_device')
|
|
return '<Setting([%s:%s] %s:%s=%s)>' % (self._rw.kind, self._validator.kind, self._device.codename, self.name, self._value)
|
|
return '<Setting([%s:%s] %s)>' % (self._rw.kind, self._validator.kind, self.name)
|
|
__unicode__ = __repr__ = __str__
|
|
|
|
|
|
class _RegisterRW(object):
|
|
__slots__ = ['register']
|
|
|
|
kind = _NamedInt(0x01, 'register')
|
|
|
|
def __init__(self, register):
|
|
assert isinstance(register, int)
|
|
self.register = register
|
|
|
|
def read(self, device):
|
|
return device.request(0x8100 | (self.register & 0x2FF))
|
|
|
|
def write(self, device, data_bytes):
|
|
return device.request(0x8000 | (self.register & 0x2FF), data_bytes)
|
|
|
|
|
|
class _FeatureRW(object):
|
|
__slots__ = ['feature', 'read_fnid', 'write_fnid']
|
|
|
|
kind = _NamedInt(0x02, 'feature')
|
|
default_read_fnid = 0x00
|
|
default_write_fnid = 0x10
|
|
|
|
def __init__(self, feature, read_fnid=default_read_fnid, write_fnid=default_write_fnid):
|
|
assert isinstance(feature, _NamedInt)
|
|
self.feature = feature
|
|
self.read_fnid = read_fnid
|
|
self.write_fnid = write_fnid
|
|
|
|
def read(self, device):
|
|
assert self.feature is not None
|
|
return device.feature_request(self.feature, self.read_fnid)
|
|
|
|
def write(self, device, data_bytes):
|
|
assert self.feature is not None
|
|
return device.feature_request(self.feature, self.write_fnid, data_bytes)
|
|
|
|
|
|
class _BooleanValidator(object):
|
|
__slots__ = ['true_value', 'false_value', 'mask', 'write_returns_value']
|
|
|
|
kind = KIND.toggle
|
|
default_true = 0x01
|
|
default_false = 0x00
|
|
default_mask = 0xFF
|
|
|
|
def __init__(self, true_value=default_true, false_value=default_false, mask=default_mask, write_returns_value=False):
|
|
self.true_value = true_value
|
|
self.false_value = false_value
|
|
self.mask = mask
|
|
self.write_returns_value = write_returns_value
|
|
|
|
def validate_read(self, reply_bytes):
|
|
reply_value = ord(reply_bytes[:1]) & self.mask
|
|
return reply_value == self.true_value
|
|
|
|
def prepare_write(self, value):
|
|
# FIXME: this does not work right when there is more than one flag in
|
|
# the same register!
|
|
return self.true_value if value else self.false_value
|
|
|
|
def validate_write(self, value, reply_bytes):
|
|
if self.write_returns_value:
|
|
reply_value = ord(reply_bytes[:1]) & self.mask
|
|
return reply_value == self.true_value
|
|
|
|
# just assume the value was written correctly, otherwise there would not
|
|
# be any reply_bytes to check
|
|
return bool(value)
|
|
|
|
|
|
class _ChoicesValidator(object):
|
|
__slots__ = ['choices', 'write_returns_value']
|
|
|
|
kind = KIND.choice
|
|
|
|
def __init__(self, choices, write_returns_value=False):
|
|
assert isinstance(choices, _NamedInts)
|
|
self.choices = choices
|
|
self.write_returns_value = write_returns_value
|
|
|
|
def validate_read(self, reply_bytes):
|
|
assert self.choices is not None
|
|
reply_value = ord(reply_bytes[:1])
|
|
valid_value = self.choices[reply_value]
|
|
assert valid_value is not None, "%: failed to validate read value %02X" % (self.__class__.__name__, reply_value)
|
|
return valid_value
|
|
|
|
def prepare_write(self, value):
|
|
assert self.choices is not None
|
|
choice = self.choices[value]
|
|
if choice is None:
|
|
raise ValueError("invalid choice " + repr(value))
|
|
assert isinstance(choice, _NamedInt)
|
|
return choice.bytes(1)
|
|
|
|
def validate_write(self, value, reply_bytes):
|
|
assert self.choices is not None
|
|
if self.write_returns_value:
|
|
reply_value = ord(reply_bytes[:1])
|
|
choice = self.choices[reply_value]
|
|
assert choice is not None, "failed to validate write reply %02X" % reply_value
|
|
return choice
|
|
|
|
# just assume the value was written correctly, otherwise there would not
|
|
# be any reply_bytes to check
|
|
return self.choices[value]
|
|
|
|
#
|
|
#
|
|
#
|
|
|
|
def register_toggle(name, register,
|
|
true_value=_BooleanValidator.default_true, false_value=_BooleanValidator.default_false,
|
|
mask=_BooleanValidator.default_mask, write_returns_value=False,
|
|
label=None, description=None):
|
|
rw = _RegisterRW(register)
|
|
validator = _BooleanValidator(true_value=true_value, false_value=false_value, mask=mask, write_returns_value=write_returns_value)
|
|
return _Setting(name, rw, validator, label=label, description=description)
|
|
|
|
|
|
def register_choices(name, register, choices,
|
|
kind=KIND.choice, write_returns_value=False,
|
|
label=None, description=None):
|
|
assert choices
|
|
rw = _RegisterRW(register)
|
|
validator = _ChoicesValidator(choices, write_returns_value=write_returns_value)
|
|
return _Setting(name, rw, validator, kind=kind, label=label, description=description)
|
|
|
|
|
|
def feature_toggle(name, feature,
|
|
read_function_id=_FeatureRW.default_read_fnid, write_function_id=_FeatureRW.default_write_fnid,
|
|
true_value=_BooleanValidator.default_true, false_value=_BooleanValidator.default_false,
|
|
mask=_BooleanValidator.default_mask, write_returns_value=False,
|
|
label=None, description=None):
|
|
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
|
validator = _BooleanValidator(true_value=true_value, false_value=false_value, mask=mask, write_returns_value=write_returns_value)
|
|
return _Setting(name, rw, validator, label=label, description=description)
|