class HeatX(driver.SmapDriver): def setup(self, opts): host = opts.get("Host", "10.0.50.118") self.rate = int(opts.get("Rate", 20)) self.modbus = ModbusTCP(host) self.add_timeseries('/energy0', 'BTU', data_type="double") self.add_timeseries('/energy1', 'BTU', data_type="double") self.add_timeseries('/accum0', 'BTU', data_type="double") self.add_timeseries('/accum1', 'BTU', data_type="double") self.add_timeseries('/volume', 'ga', data_type="double") self.add_timeseries('/accum_volume', 'ga', data_type="double") self.add_timeseries('/power', 'BTU/hr', data_type="double") self.add_timeseries('/vol_flow', 'ga/min', data_type="double") self.add_timeseries('/temp_flow', 'f', data_type="double") self.add_timeseries('/temp_return', 'f', data_type="double") self.add_timeseries('/tdelta', 'f', data_type="double") self.set_metadata( '/', { 'Instrument/Manufacturer': 'Central Station Steam Co.', 'Instrument/Model': 'Cadillac HEATX BTU Meter' }) def start(self): util.periodicSequentialCall(self.update).start(self.rate) def update(self): try: data = self.modbus.read(25, 36) except IOError, e: log.err("Exception while reading device: " + str(e)) return if data == None: log.err("Failed to read device\n") return s = "" try: for i in range(0, len(data), 4): s += data[i + 1] s += data[i + 0] s += data[i + 3] s += data[i + 2] vals = struct.unpack("<18f", s) except: return t = util.now() self.add("/energy0", t, vals[0]) self.add("/energy1", t, vals[1]) self.add("/accum0", t, vals[2]) self.add("/accum1", t, vals[3]) self.add("/volume", t, vals[4]) self.add("/accum_volume", t, vals[5]) self.add("/power", t, vals[10]) self.add("/vol_flow", t, vals[12]) self.add("/temp_flow", t, vals[14]) self.add("/temp_return", t, vals[15]) self.add("/tdelta", t, vals[16])
def newreading(): global counter #print '-'*50 s.get_collection('/')['Metadata']['Location'] = {'Room': counter} s.get_collection('/').dirty_children() for i in xrange(0, 1): # s.get_timeseries('/sensor0')._add(util.now(), counter) # s.get_timeseries('/sensor1')._add(counter) s._add('/sensor0', util.now(), counter) s._add('/sensor1', counter) counter += 1
def newreading(): global counter #print '-'*50 s.get_collection('/')['Metadata']['Location'] = {'Room' : counter} s.get_collection('/').dirty_children() for i in xrange(0, 1): # s.get_timeseries('/sensor0')._add(util.now(), counter) # s.get_timeseries('/sensor1')._add(counter) s._add('/sensor0', util.now(), counter) s._add('/sensor1', counter) counter += 1
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)
def add_job(self, job): j = SmapJob(job) if 'StartTime' in job: start = job['StartTime'] / 1000. wait = start - util.now() else: wait = 0 assert wait >= 0 actions = j.actions if j.after: previous_job = util.find(lambda x: x.name == j.after, self.jobs) if previous_job is None: raise util.SmapException("No job named %s") % j.after else: j.d_outer = previous_job.d_outer j.job_id = previous_job.job_id j.uuid = job['uuid'] self._job_ids[j.uuid] = j.job_id else: # assign it its own deferred j.d_outer = defer.Deferred() # closure that will carry out all of the job's actions def act(_): for action in actions: path = action['Path'] state = action['State'] actuator = self.inst.get_timeseries(path) print 'Setting', path, 'to', state actuator.impl.set_state(None, state) # queue the callback j.d_outer.addCallback(act) print 'Added callback to', j.d_outer if not j.after: # job_id will let you cancel it j.job_id = reactor.callLater(wait, j.d_outer.callback, None) self._job_ids[job['uuid']] = j.job_id self.jobs.append(j) return j.d_outer
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 pause_reporting(self): return self.reports.pause() def unpause_reporting(self): return self.reports.unpause() if __name__ == '__main__': ROOT_UUID = uuid.uuid1() s = SmapInstance(ROOT_UUID) s.add_collection("/steve") t = Timeseries(s.uuid("sdh"), "V", buffersz=2) s.add_timeseries("/sensor0", t) s.set_metadata("/sensor0", {"Foo": "Bar", "Baz": 10}) t.add(util.now(), 12) t.add(util.now(), 13) print s.get_timeseries(t['uuid']) print s.get_timeseries('/sensor0') print s.get_timeseries('/') # s.get_collection('/').set_metadata({'Extra' : {"foo": " bar"}}) print s.get_collection('/') # print "Finding all Timeseries under /" print s._lookup_r('/', pred=ITimeseries.providedBy) print s.lookup('/+Timeseries') print s._lookup_r('/', pred=lambda x: x.dirty) # print s._lookup_r("/foo")
# easy-add -- create a timeseries automatically. kwargs pass through # to the timeseries so you can change the data type, description, etc. # # the parent must exist and be a collection for this to work. # # arg0 : path to add at # arg1 : either a unique string (key) or a uuid instance # arg2 : units s.add_timeseries("/sensor0", "sdh", "V") # alternative -- add an existing timeseries s.add_timeseries("/sensor1", core.Timeseries(s.uuid("sdh2"), "F", buffersz=2)) # add readings to a timeseries # get_timeseries will look up based on either path or uuid s.get_timeseries("/sensor0").add(util.now(), 12) s.get_timeseries("/sensor0").add(util.now(), 13) # you can set timeseries properties by accessing it as a dict. The # changes you make must follow the smap schema and you will get a # SmapSchemaException if you try to write an invalid object. s.get_timeseries("/sensor0")['Metadata'] = \ {'Instrument' : { 'Manufacturer' : "Stephen Dawson-Haggerty" }, 'Extra' : { 'Sucks' : 'Andrew' } } s.get_collection("/")["Metadata"] = {"Extra" : {"foo" : "bar"} }
self.s.send(cmd + "\r") reply = self.s.recv(1024) self.s.close() self.s = None except IOError, e: log.err() return else: if reply.startswith(cmd[1:]): val = float(reply[len(cmd) - 1:-1]) # log.msg("read: " + str(val)) if val == None: time.sleep(0.5) log.err("Failed to update reading") return else: return this_time = util.now() # accumulate readings if self.last_time: self.accum += (self.last_time[1] + val) * ( (this_time - self.last_time[0]) / 60.) * 0.5 # and output a reading ever RATE seconds if this_time - self.last_add > self.rate: self.add('/0', this_time, float(val)) self.add('/1', this_time, float(self.accum)) self.last_add = this_time self.last_time = (this_time, val)
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 unpause_reporting(self): return self.reports.unpause() if __name__ == '__main__': ROOT_UUID = uuid.uuid1() s = SmapInstance(ROOT_UUID) s.add_collection("/steve") t = Timeseries(s.uuid("sdh"), "V", buffersz=2) s.add_timeseries("/sensor0", t) s.set_metadata("/sensor0", { "Foo" : "Bar", "Baz" : 10 }) t.add(util.now(), 12) t.add(util.now(), 13) print s.get_timeseries(t['uuid']) print s.get_timeseries('/sensor0') print s.get_timeseries('/') # s.get_collection('/').set_metadata({'Extra' : {"foo": " bar"}}) print s.get_collection('/') # print "Finding all Timeseries under /" print s._lookup_r('/', pred=ITimeseries.providedBy) print s.lookup('/+Timeseries') print s._lookup_r('/', pred=lambda x: x.dirty)
# to the timeseries so you can change the data type, description, etc. # # the parent must exist and be a collection for this to work. # # arg0 : path to add at # arg1 : either a unique string (key) or a uuid instance # arg2 : units s.add_timeseries("/sensor0", "sdh", "V") # alternative -- add an existing timeseries s.add_timeseries("/sensor1", core.Timeseries(s.uuid("sdh2"), "F", buffersz=2)) # add readings to a timeseries # get_timeseries will look up based on either path or uuid s.get_timeseries("/sensor0").add(util.now(), 12) s.get_timeseries("/sensor0").add(util.now(), 13) # you can set timeseries properties by accessing it as a dict. The # changes you make must follow the smap schema and you will get a # SmapSchemaException if you try to write an invalid object. s.get_timeseries("/sensor0")['Metadata'] = \ {'Instrument' : { 'Manufacturer' : "Stephen Dawson-Haggerty" }, 'Extra' : { 'Sucks' : 'Andrew' } } s.get_collection("/")["Metadata"] = {"Extra": {"foo": "bar"}}
try: self.s.send(cmd + "\r") reply = self.s.recv(1024) self.s.close() self.s = None except IOError, e: log.err() return else: if reply.startswith(cmd[1:]): val = float(reply[len(cmd) - 1:-1]) # log.msg("read: " + str(val)) if val == None: time.sleep(0.5) log.err("Failed to update reading") return else: return this_time = util.now() # accumulate readings if self.last_time: self.accum += (self.last_time[1] + val) * ((this_time - self.last_time[0]) / 60.) * 0.5 # and output a reading ever RATE seconds if this_time - self.last_add > self.rate: self.add('/0', this_time, float(val)) self.add('/1', this_time, float(self.accum)) self.last_add = this_time self.last_time = (this_time, val)