Ejemplo n.º 1
0
    def add_collection(self, path, *args):
        """Add collection to the namespace.  For instance::

          inst.add_collection('/c1')

        :param string path: path under which to add the collection
        :param args[0]: :py:class:`Collection` class to add, if present
        :rtype: the :py:class:`Collection` which was added
        :raises: :py:class:`SmapException` if the parent is not a
         collection, or the path exists.
"""
        if len(args) > 0 and ICollection.providedBy(args[0]):
            collection = args[0]
        elif len(args) == 0:
            collection = Collection(path, self)
        else:
            raise SmapException("add_collection: wrong number of arguments")

        path = util.split_path(path)
        if len(path) > 0:
            parent = self.get_collection(util.join_path(path[:-1]))
            if not parent:
                raise SmapException(
                    "add_collection: parent is not collection!")
            parent.add_child(path[-1])
        if util.join_path(path) in self.OBJS_PATH:
            raise SmapException("add_timeseries: path " + str(path) +
                                " exists!")

        self.OBJS_PATH[util.join_path(path)] = collection
        if not self.loading: self.reports.update_subscriptions()
        return collection
Ejemplo n.º 2
0
    def add_timeseries(self, path, *args, **kwargs):
        """Add a timeseries to the smap server at the given path.  This will
        generate a UUID for the timeseries.

        direct form 
        :param path a Timeseries instance
        
        simple form 
        :param args[0] is a uuid instance, or a key to generate a uuid with by combining it with the root uuid.
        :param args[1] and kwargs are arguments passed to the Timeseries constructor.  Therefore you have to include at least the UnitofMeasure

        :param boolean replace: (kwarg) replace an existing timeseries at that path instead of throwing an exception
        :param boolean recurse: recursively create parent collections instead of thrwoing an exception.  Default is True.

        :raises: :py:class:`SmapException` if the parent isn't a collection or the path already exists.
        """ 
        replace = kwargs.pop('replace', False)
        recurse = kwargs.pop('recurse', True)
        klass = kwargs.pop('klass', Timeseries)
        if len(args) == 0 or \
                not ITimeseries.providedBy(args[0]) and not IActuator.providedBy(args[0]):
            if len(args) == 2:
                if not isinstance(args[0], uuid.UUID):
                    id = self.uuid(args[0], namespace=kwargs.get('namespace', None))
                else:
                    id = args[0]
                args = args[1:]
            elif len(args) == 1:
                id = self.uuid(util.norm_path(path), kwargs.get('namespace', None))
            else:
                id = self.uuid(util.norm_path(path))
#                 raise SmapException("SmapInstance.add_timeseries may only be called "
#                                     "with two or three arguments")

            kwargs.pop('namespace', None)
            timeseries = klass(id, *args, **kwargs)
            if id != args[0]:
                setattr(timeseries, "key", args[0])
        else:
            timeseries = args[0]

        path = util.split_path(path)
        if recurse: self._add_parents(path)
        parent = self.get_collection(util.join_path(path[:-1]))

        if not replace and util.join_path(path) in self.OBJS_PATH:
            raise SmapException("add_timeseries: path " + str(path) + " exists!")
        if not parent:
            raise SmapException("add_timeseries: parent is not a collection!")
        parent.add_child(path[-1])

        # place the new timeseries into the uuid and path tables
        self.OBJS_UUID[timeseries['uuid']] = timeseries
        self.OBJS_PATH[util.join_path(path)] = timeseries
        timeseries.inst = self
        setattr(timeseries, 'path', util.join_path(path))
        if not self.loading: self.reports.update_subscriptions()
        return timeseries
Ejemplo n.º 3
0
 def uuid(self, key, namespace=None):
     if not namespace:
         namespace = self.root_uuid
     if key and namespace:
         rv = uuid.uuid5(namespace, key)
         if rv in self.OBJS_UUID:
             raise SmapException(
                 "Duplicate UUID detected -- this probably "
                 "means you specifed the same key twice in "
                 "the same namespace.  The offending key is " + str(key))
         return rv
     else:
         raise SmapException("Timeseries cannot generate uuid: must "
                             "specifiy either uuid or key and ROOT_UUID")
Ejemplo n.º 4
0
    def render_write(self, request):
        """Render a request to change the state"""
        if 'state' in request.args and len(request.args['state']) > 0:
            new_state = self.impl.parse_state(request.args['state'][0])
            if not self.impl.valid_state(new_state):
                raise SmapException("Invalid state: " + str(new_state), 400)

            allowed, rv = self.writer(request, new_state)
            if allowed:
                if not issubclass(rv.__class__, defer.Deferred):
                    rv = defer.succeed(rv)
                rv.addCallback(lambda x: self._finish_render(request, x))
                return rv
            else:
                raise SmapException("Cannot actuate now due to rate limit\n", 503)
Ejemplo n.º 5
0
 def __setitem__(self, attr, value):
     if not attr in ['Contents', 'Metadata', 'Proxy']:
         raise SmapException("Key " + attr + " cannot be set on a Collection!")
     elif not attr in self or value != dict.__getitem__(self, attr):
         self.dirty_children()
         dict.__setitem__(self, attr, value)
         self.dirty_children()
Ejemplo n.º 6
0
 def add(self, path, *args, **kwargs):
     """Utility to call the version of :py:meth:`~smap.core.Timeseries.add`
     associated with *path*.  The same as ``inst.get_timeseries(path).add(...)``
     """
     try:
         return self.get_timeseries(path).add(*args, **kwargs)
     except AttributeError, e:
         raise SmapException("add failed: no such path: %s" % path)
Ejemplo n.º 7
0
    def set_metadata(self, path, *metadata):
        if len(metadata) > 1:
            metadata = dict([metadata])
        else: metadata = metadata[0]

        for v in metadata.itervalues():
            if not util.is_string(v):
                raise SmapException("set_metadata: values must be strings!")

        o = self.lookup(path)
        o.set_metadata(metadata)
Ejemplo n.º 8
0
    def _finish_render(self, request, state):
        # finish by adding the current state as the reading
        if isinstance(state, dict):
            return state
        elif hasattr(state, "__iter__"):
            now, val = state
        else:
            now, val = util.now() * 1000, state

        try:
            val = self.impl.translate_state(state)
        except Exception, e:
            raise SmapException("Error processing write result: " + str(e), 500)
Ejemplo n.º 9
0
    def render_read(self, request):
        """Render the read

        The rate limiter will make sure that we don't overload the
        device; it will used the Timeseries cached value if we've
        called it too much.
        """
        allowed, rv = self.reader(request)
        if allowed:
            if not issubclass(rv.__class__, defer.Deferred):
                rv = defer.succeed(rv)
            rv.addCallback(lambda x: self._finish_render(request, x))
            return rv
        else:
            raise SmapException("Cannot actuate now due to rate limit", 503)
Ejemplo n.º 10
0
    def _add(self, *args):
        """Add a new reading to this timeseries.  This version must
only be called from the :py:mod:`twisted` main loop; *i.e.* from a
callback added with ``reactor.callFromThread()``

Can be called with 1, 2, or 3 arguments.  The forms are

* ``_add(value)``
* ``_add(time, value)``
* ``_add(time, value, seqno)``

:raises SmapException: if the value's type does not match the stream
 type, or was called with an invalid number of arguments.
        """
        seqno = None
        if len(args) == 1:
            time = util.now()
            if self.milliseconds: time *= 1000
            value = args[0]
        elif len(args) == 2:
            time, value = args
        elif len(args) == 3:
            time, value, seqno = args
        else:
            raise SmapException("Invalid add arguments: must be (value), "
                                "(time, value), or (time, value, seqno)")

        # note that we got data now
        self.inst.statslog.mark()

        time = int(time)
        if not self.milliseconds:
            time *= 1000

        if not self._check_type(value):
            raise SmapException("Attempted to add " + str(value) +
                                " to Timeseries, but " +
                                "the timeseries type is " +
                                self.__getitem__('Properties')['ReadingType'])

        if seqno: reading = time, value, seqno
        else: reading = time, value
        self["Readings"].append(reading)
        if not hasattr(self, 'inst'): return

        # if a timeseries is dirty, we need to republish all of its
        # metadata before we publish it so stream is right. some of
        # this may have already been published, in which case it won't
        # actually do anything.
        if self.dirty:
            split_path = util.split_path(getattr(self, 'path'))
            for i in xrange(0, len(split_path)):
                path_seg = util.join_path(split_path[:i])
                self.inst.reports.publish(path_seg,
                                          self.inst.get_collection(path_seg))
            rpt = dict(self)
            rpt['Readings'] = [reading]
            self.inst.reports.publish(getattr(self, 'path'), rpt)
            self.dirty = False
        else:
            # publish a stripped-down Timeseries object
            self.inst.reports.publish(getattr(self, 'path'), {
                'uuid': self['uuid'],
                'Readings': [reading]
            })