218 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			6.8 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_value(self, reply_bytes, expected_value):
 | |
| 		if isinstance(expected_value, int):
 | |
| 			return ord(reply_bytes[:1]) & self.mask == expected_value
 | |
| 		else:
 | |
| 			for i in range(0, len(self.mask)):
 | |
| 				masked_value = ord(reply_bytes[i:i+1]) & ord(self.mask[i:i+1])
 | |
| 				if masked_value != ord(expected_value[i:i+1]):
 | |
| 					return False
 | |
| 			return True
 | |
| 
 | |
| 	def validate_read(self, reply_bytes):
 | |
| 		return self._validate_value(reply_bytes, 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:
 | |
| 			return self._validate_value(reply_bytes, 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)
 |