class DataSource(object): """ Abstract data source model """ debug = False timeout = -1 # no timeout by default #TODO def __init__(self, name=None, unit=None, timeout=None): self.name = name or self.__class__.__name__ self._logger = logging.getLogger("gsensors.%s" % self.name) self.events = Events() self.unit = unit self._value = 0 self._error = None if timeout is not None: self.timeout = timeout self.last_update = None # datetime on last update @property def value(self): return self._value @value.setter def value(self, val): """ Set/Update the value """ now = datetime.now() self.set_value(val, update_time=now) def set_value(self, val, update_time=None): self.events.on_update(self, val) if val != self._value: self._value = val self.last_update = update_time self.events.on_change(self, val) @property def error(self): return self._error @error.setter def error(self, err): if err != self._error: # call error listener old_err = self._error self._error = err if self._error is not None: self.events.on_error(self, err) else: self.events.on_error_release(self, old_err) def _checked_callback(self, callback): def wrapper(*args, **kwargs): try: callback(*args, **kwargs) except Exception as err: self.error = "Callback error" self._logger.error("Callback error: %s" % err, exc_info=full_exc_info()) return wrapper def _callback_wrap_onvalue(self, callback, value): if callable(value): def wrapper(source, new_value): if value(new_value): callback(new_value) else: def wrapper(source, new_value): #self._logger.debug("%s =? %s" % (new_value, value)) if new_value == value: callback(new_value) return wrapper def on_update(self, callback, value=None): """ Callback when value is updated (even if it stays the same). If `value` is given the callback will be called only if the new value equals it. """ if value is not None: callback = self._callback_wrap_onvalue(callback, value) callback = self._checked_callback(callback) self.events.on_update += callback def on_change(self, callback, value=None): """ Callback when value changed. If `value` is given the callback will be called only if the new value equals it. """ if value is not None: callback = self._callback_wrap_onvalue(callback, value) callback = self._checked_callback(callback) self.events.on_change += callback def on_timeout(self, callback): #TODO raise NotImplementedError def on_error(self, callback): """ Callback when an error occurs (property error changed and is not None) """ callback = self._checked_callback(callback) self.events.on_error += callback def on_error_release(self, callback): """ Callback when there is no more error (property error changed back to None) """ callback = self._checked_callback(callback) self.events.on_error_release += callback def start(self): pass def export(self): """ Data given to the clients on each change """ res = {} res["type"] = self.__class__.__name__ res["name"] = self.name res["timeout"] = self.timeout res["value"] = self.value res["unit"] = self.unit res["error"] = self.error if self.last_update is not None: res["last_update"] = self.last_update.isoformat() return res def desc(self): res = {} res["type"] = self.__class__.__name__ res["name"] = self.name return res