Ejemplo n.º 1
0
 def __init__(self, port, max_conns):
     self.server = Server(port, max_conns)
     self.server.on_data += self._data_handler
     self.server.on_cleanup += self._clean_contact
     self.subscribers = {}
     self.properties = {}
     self._properties_lock = Lock()
     self._subscriber_lock = Lock()
     self._updated_properties = set()
     # Update Vars
     self._last_update = datetime.now()
     self._updating = False
     self._update_lock = Lock()
     # Handlers
     self.proto = protocol.get()
     for x in filter(lambda y: y.mtype == protocol.client,
                     self.proto.values()):
         x.handler = self.__getattribute__('_%s_handler' % x.name)
Ejemplo n.º 2
0
class UIServer():
    Logger = logger.get(__name__)

    def __init__(self, port, max_conns):
        self.server = Server(port, max_conns)
        self.server.on_data += self._data_handler
        self.server.on_cleanup += self._clean_contact
        self.subscribers = {}
        self.properties = {}
        self._properties_lock = Lock()
        self._subscriber_lock = Lock()
        self._updated_properties = set()
        # Update Vars
        self._last_update = datetime.now()
        self._updating = False
        self._update_lock = Lock()
        # Handlers
        self.proto = protocol.get()
        for x in filter(lambda y: y.mtype == protocol.client,
                        self.proto.values()):
            x.handler = self.__getattribute__('_%s_handler' % x.name)

    def add_property(self, name, path, object_root):
        obj = object_root
        for item in path.split('.'):
            obj = obj.__getattribute__(item)
        with self._properties_lock:
            if name not in self.properties:
                self.properties[name] = obj
                obj.on_changed += self.on_property_update

    def send_data(self, sub,  message_name, data):
        message = self.proto[message_name].pack(data)
        self.server.send_data(sub.address, message)

    #: Min time between updates
    UPDATE_DELTA = timedelta(seconds=2)

    def try_update(self):
        """
        Function that checks to see if an update needs to be started.
        This is locked for multithreading,
        and spawns another thread to do the update.
        """
        with self._update_lock:
            if (datetime.now() - self.UPDATE_DELTA > self._last_update
                    and not self._updating):
                self._updating = True
                self._last_update = datetime.now()
                # Get the party started
                t = Thread(target=self._update)
                t.daemon = True
                t.start()

    def _update(self):
        try:
            with self._properties_lock:
                props = self._updated_properties
                self._updated_properties = set()
            # Used because transforming some of these items is expensive.
            # This could be done in one huge ass expression,
            # but that may be unreadable.
            self.Logger.debug("Subcribers: %s", self.subscribers)
            with self._subscriber_lock:
                subbed_props = reduce(lambda x, y: x.union(y),
                                      (x.subscriptions for x
                                       in self.subscribers.values()),
                                      set()).intersection(props)
                cached_vals = {x: self.properties[x].flatten()
                               for x in subbed_props}
                # Check if anything changed
                if len(cached_vals) > 0:
                    for sub in self.subscribers.values():
                        # Collect all data the client is subscribed to.
                        data = reduce(lambda x, y: dict(x, **y),
                                      (cached_vals[x] for x in props
                                       if x in sub.subscriptions),
                                      {})
                        # Send data to the clients
                        if len(data) > 0:
                            self.send_data(sub, 'update', data)
        finally:
            self._updating = False

    def on_property_update(self, name, value):
        """
        Handler for when a watched property is updated.
        We only store the name, since the value may change a lot.
        The value is also of unknown type, not the json-compatible
        types needed.
        """
        with self._properties_lock:
            self.Logger.debug("updating %s (%s)", name, value)
            self._updated_properties.add(name)
        self.try_update()

    def get_subscriber(self, address):
        """
        Gets the subscriber at the given address.
        If none exists, one is created.
        """
        try:
            client = self.subscribers[str(address)]
        except KeyError:
            client = Client(address)
            with self._subscriber_lock:
                self.subscribers[str(address)] = client
        return client

    def _clean_contact(self, address):
        """
        Handler for cleaning up a contact once they disconnect.
        This is processed from the quitting listener's thread.
        """
        with self._subscriber_lock:
            del(self.subscribers[str(address)])

    def _data_handler(self, address, payload):
        """
        Handler for when data is received.
        """
        subscriber = self.get_subscriber(address)
        try:
            cmd_name = payload['command']
            data = payload['values']
            self.proto[cmd_name].handler(subscriber, data)
        except Exception as e:
            self.Logger.error(traceback.format_exc())

    def _get_events_handler(self, subscriber, data):
        names = [x for x in self.properties.keys()]
        self.Logger.debug("Events: %s", names)
        self.send_data(subscriber, 'events', names)

    def _subscribe_handler(self, subscriber, data):
        for value in (x for x in data if x in self.properties):
            # Check only to look for a data update
            if value not in subscriber.subscriptions:
                subscriber.subscriptions.add(value)
                self.send_data(subscriber, 'update',
                               self.properties[value].flatten())

    def _unsubscribe_handler(self, subscriber, data):
        for value in (x for x in data if x in self.properties):
            try:
                subscriber.subscriptions.remove(value)
            except KeyError:
                pass