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
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
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
def explore(item, path): if not 'Contents' in item: item.dirty = True else: for ps in item['Contents']: newpath = path + [ps] explore(self.inst.lookup(util.join_path(newpath)), newpath)
class InstanceResource(resource.Resource): """Resource which maps HTTP requests to requests on the sMAP instance. """ def __init__(self, inst): self.inst = inst resource.Resource.__init__(self) isLeaf = True def render_GET(self, request): request.setHeader('Content-type', 'application/json') # assemble the results try: obj = self.inst.lookup(util.join_path(request.postpath)) except Exception, e: import traceback traceback.print_exc() setResponseCode(request, exception, 500) request.finish() if obj == None: request.setResponseCode(404) return ("No such timeseries or collection: " + util.join_path(request.postpath) + '\n') else: d = defer.maybeDeferred(core.SmapInstance.render_lookup, request, obj) d.addCallback(lambda x: self.send_reply(request, x)) d.addErrback(lambda x: self.send_error(request, x)) return server.NOT_DONE_YET
def render_PUT(self, request): request.setHeader('Content-type', 'application/json') # you can only PUT actuators obj = self.inst.lookup(util.join_path(request.postpath)) d = defer.maybeDeferred(core.SmapInstance.render_lookup, request, obj) d.addCallback(lambda x: self.send_reply(request, x)) d.addErrback(lambda x: self.send_error(request, x)) return server.NOT_DONE_YET
def render_GET(self, request): request.setHeader('Content-type', 'application/json') # assemble the results try: obj = self.inst.lookup(util.join_path(request.postpath)) except Exception, e: import traceback traceback.print_exc() setResponseCode(request, exception, 500) request.finish()
def lookup(self, id, pred=None): """Retrieve an object in the resource hierarchy by path or uuid. If *id* is a string not starting with ``/``, it will be passed to the :py:class:`uuid.UUID` constructor; otherwise it will be treated as a pathname. *pred* is an optional predicate which can be used to test the result. """ if util.is_string(id): path = util.split_path(id) if len(path) > 0 and path[-1][0] == "+": return self._lookup_r(util.join_path(path[:-1]), pred=pred) else: obj = self.OBJS_PATH.get(util.join_path(path), None) elif isinstance(id, uuid.UUID): return self.OBJS_UUID.get(id, None) else: obj = None if not pred or pred(obj): return obj else: return None
def writeDROMScsv(self, value): fcsv = open('meterdata.csv','w') fcsv.write(','.join(['DateTime', 'MeterId', 'Value1', 'Value2']) + '\n') for path, val in value: if not 'Readings' in val: continue cmps = split_path(path) channel = join_path(cmps[1:]) for d in val['Readings']: if d is None: continue ts = dtutil.strftime_tz(dtutil.ts2dt(d[0] / 1000), "%Y-%m-%d %H:%M", tzstr='Local') if ts is None: continue v = d[1] if v is None: continue if val['Properties']['UnitofMeasure']=='Watts': v /= 1000. v /= 4. # approximate kWh fcsv.write(','.join([ts,channel,str(v)]) + '\n') fcsv.close()
def _add_parents(self, path): for i in xrange(0, len(path)): if not self.get_collection(util.join_path(path[:i])): self.add_collection(util.join_path(path[:i]))
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]})
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] })