コード例 #1
0
ファイル: MasterDB.py プロジェクト: kmpeters/epicsarchiver
    def add_alert(self, pvname=None,name=None,
                  mailto=None,  mailmsg=None, timeout=30,
                  compare='ne', trippoint=None, **kw):
        """add  a new alert"""
        if pvname is None: return
        
        pvname = normalize_pvname(pvname)        
        if name is None: name = pvname
        if len(self.pvnames)== 0: self.get_pvnames()
        if pvname not in self.pvnames:
            self.add_pv(pvname)

        active = 'yes'
        if mailto  is None:    active,mailto = ('no','')
        if mailmsg  is None:   active,mailmsg= ('no','')
        if trippoint is None:  active,trippoint = ('no',0.)

        if compare not in self.optokens: compare = 'ne'
        
        self.alerts.insert(name=name,pvname=pvname,active=active,
                           mailto=mailto,mailmsg=mailmsg, timeout=30,
                           compare=compare, trippoint=trippoint)
                  
        for a in self.get_alerts(pvname=pvname,name=name):
            val = epics.caget(pvname)
            self.check_alert(a['id'],val)
コード例 #2
0
ファイル: Instruments.py プロジェクト: jdigilio/epicsarchiver
    def add(self,pvname=None,name=None,
            mailto=None,  mailmsg=None,
            compare='ne', trippoint=None, **kw):

        if pvname is None:
            print 'must provide a pvname for an alert'
            return
        
        pvname = normalize_pvname(pvname)        
        if name is None: name = pvname
        if len(self.pvnames)== 0: self.get_pvnames()
        if pvname not in self.pvnames: self.add_pv(pvname)

        active = 'yes'
        if mailto  is None:
            mailto  =''
            active  = 'no'
        if mailmsg is None:
            mailmsg =''
            active  = 'no'            
        if trippoint is None:
            trippoint = 0.
            active = 'no'
        if compare in self.comps: compare = 'ne'
        
        self.alerts.update(name=name,pvname=pvname,active=active,
                           mailto=mailto,mailmsg=mailmsg,
                           compare=compare, trippoint=trippoint)
コード例 #3
0
ファイル: HTMLWriter.py プロジェクト: kmpeters/epicsarchiver
    def setup(self,active_tab=None,formkeys=None,debug=False, helpsection='main', **kw):
        """ update self.kw with passed keywords, using
        'web form' versions as defaults (see below). Also
        starts the html and shows starting links

        For each key in formkeys:
            if the self.kw has an empty value ('' or None)
            the 'form_%s' % key is used as a default value
            and self[key] is set to the 'form_%s' version

        """
        self.kw.update(kw)
        if formkeys is not None and len(formkeys)>0:
            for key in formkeys:
                val = clean_input(self.kw.get(key,''))
                if val == '':
                    val = clean_input(self.kw.get("form_%s" % key,''))
                    if val != '':
                        self.kw[key] = val

        self.starthtml()

        inst = self.kw.get('inst_id',-1)
        pv = self.kw.get('pv','')
        pv = normalize_pvname(pv)

        self.show_links(pv=pv,inst=inst,help=helpsection,active_tab=active_tab)
        if debug: self.show_dict(self.kw)
        return 
コード例 #4
0
ファイル: MasterDB.py プロジェクト: kmpeters/epicsarchiver
    def drop_pv(self,pvname):
        """drop a PV from the caching process -- really this 'suspends updates'
        will take effect once a 'process_requests' is executed."""
        npv = normalize_pvname(pvname)
        if len(self.pvnames)== 0: self.get_pvnames()
        if not npv in self.pvnames: return

        cmd = "insert into requests (pvname,action) values ('%s','suspend')" % npv
        self.db.execute(cmd)
コード例 #5
0
ファイル: MasterDB.py プロジェクト: kmpeters/epicsarchiver
 def get_instruments_with_pv(self,pv):
     """ return a list of (instrument ids, inst name, station name) for
     instruments which  contain a named pv"""
     inames = []
     pvn = normalize_pvname(pv)
     for r in self.inst_pvs.select(where="pvname='%s'" % pvn):
         inst = self.instruments.select_one(where="id=%i" % r['inst'])
         sta  = self.stations.select_one(where="id=%i" % inst['station'])
         inames.append((r['inst'],inst['name'],sta['name']))
     return inames
コード例 #6
0
ファイル: MasterDB.py プロジェクト: kmpeters/epicsarchiver
    def get_alerts(self,pvname=None,name=None):
        """ return a list of alerts for a pvname"""
        where = '1=1'
        if pvname is not None: 
            pvname = normalize_pvname(pvname)
            where = "pvname='%s'" % pvname
        
        if name is not None:
            where = "%s and name='%s'" % (where,clean_input(name))

        return self.alerts.select(where=where)
コード例 #7
0
ファイル: WebStatus.py プロジェクト: jdigilio/epicsarchiver
    def show_pvfile(self,file):
        try:
            f = open(file,"r")
            lines = f.readlines()
            f.close()
        except:
            lines = ['', '']
        for s in lines:
            s = s.strip()
            if s.startswith('#') or len(s) < 2:
                continue
            elif s.startswith('['):
                i = s.find(']')
                if i == -1: i= len(s)
                self.table_label(s[1:i])
            elif s.startswith('--'):
                self.write(self.hrule_row)
            elif s.startswith('<>'):
                self.write(self.space_row)
            else:
                cline = s.split('|')
                pvnames = [normalize_pvname(word.strip()) for word in cline[0].split(',')]
                desc, format, outtype = None, None, None
                if len(cline) > 1:
                    desc = cline[1].strip()
                    if len(desc)==0:
                        desc = None
                    if len(cline) > 2:
                        format = cline[2].strip()
                if format == 'yes/no':
                    format = None
                    outtype = 'yes/no'
                    
                if len(pvnames) > 0:
                    vals = []
                    labs = []
                    for pvname in pvnames:
                        # pvname = pvname.strip()
                        (label,val) =  self.get_pv(pvname,format=format,desc=desc,outtype=outtype)
                        # self.write("<tr>  <td>:: %s</td><td>%s</td></tr>" % (pvname,str(val)))

                        if (label is None): label = pvname
                        if (label.startswith('"') and label.endswith('"') or
                            label.startswith("'") and label.endswith("'")):
                            label = label[1:len(label)-1]
                        labs.append(label)
                        vals.append(val)
                    if len(pvnames) > 1:
                        if desc is None: desc = labs
                        self.linked_row(desc, pvnames, vals)
                        # self.cache.set_allpairs(pvnames)
                    else:
                        if desc is None: desc = labs[0]
                        self.linked_row(desc, pvnames[0], vals[0])
コード例 #8
0
ファイル: MasterDB.py プロジェクト: kmpeters/epicsarchiver
    def request_pv_cache(self,pvname):
        """request a PV to be included in caching.
        will take effect once a 'process_requests' is executed."""
        self.db.use(master_db)
        npv = normalize_pvname(pvname)
        if len(self.pvnames)== 0: self.get_pvnames()
        if npv in self.pvnames: return

        cmd = "insert into requests (pvname,action,ts) values ('%s','add',%f)" % (npv,time.time())
        self.db.execute(cmd)
        print 'REQUEST_PV_CACHE: add ', pvname
コード例 #9
0
ファイル: Instruments.py プロジェクト: jdigilio/epicsarchiver
    def __make_where(self,id=None, name=None,pvname=None):
        where = '1=1'
        if id is not None:
            where = "%s and id=%i" % (where,int(id))

        if name is not None:
            where = "%s and name='%s'" % (where,clean_input(name))

        if pvname is not None:
            where = "%s and pvname='%s'" % (where, normalize_pvname(pvname))
        return where
コード例 #10
0
ファイル: Instruments.py プロジェクト: jdigilio/epicsarchiver
 def remove_instrument_pvs(self,pvlist,name=None,station=None,notes=''):
     insts = self.get_instruments(name=name,station=station)
     if len(insts)> 1:
         print 'multiple instruments matched name/station=%s/%s' % (name,station)
         return None
     if not isinstance(pvlist,(tuple,list)):
         print 'must provide list of PVs for instrument %s' % (name)
         return None
     inst_id = insts[0]['id']
     q = "delete from instrument_pvs where inst=%i and pvname='%s'"
     for pvname in pvlist:
         pvn = normalize_pvname(pvname)
         self.inst_pvs.db.execute(q % (inst_id, pvn))
コード例 #11
0
ファイル: Cache.py プロジェクト: kmpeters/epicsarchiver
 def get_full(self,pv,add=False):
     " return full information for a cached pv"
     npv = normalize_pvname(pv)
     if len(self.pvnames)== 0: self.get_pvnames()
     if add and (npv not in self.pvnames):
         self.add_pv(npv)
         sys.stdout.write('adding PV.....\n')
         return self.get_full(pv,add=False)
     w = self.q_getfull % npv
     try:
         return self.sql_exec_fetch(w)[0]
     except:
         return self.null_pv_value
コード例 #12
0
ファイル: Archiver.py プロジェクト: jdigilio/epicsarchiver
    def get_value_at_time(self,pvname,t):
        "return archived value of a pv at one time"
        if pvname is None: return None
        pvname = normalize_pvname(pvname)
        info = self.get_info(pvname)
        if info is None: return None

        db = self.dbs_for_time(t,t)[0]
        self.db.use(db)
        qpv  = "select data_table,id from pv where name ='%s'" % pvname
        qdat = 'select time,value from %s where pv_id=%i and time<=%f order by time desc limit 1'
        i = self.db.exec_fetchone(qpv)
        r = self.db.exec_fetchone(qdat % (i['data_table'],i['id'],t))
        return r['time'],r['value']
コード例 #13
0
ファイル: MasterDB.py プロジェクト: kmpeters/epicsarchiver
    def add_pv(self,pvname,set_motor_pairs=True):
        """adds a PV to the cache: actually requests the addition, which will
        be handled by the next process_requests in mainloop().

        Here, we check for 'Motor' PV typs and make sure all motor fields are
        requested together, and that the motor fields are 'related' by assigning
        a pair_score = 10.                
        """

        pvname = normalize_pvname(pvname.strip())
        fields = (pvname,)
        if not valid_pvname(pvname):
            sys.stdout.write("## MasterDB add_pv invalid pvname = '%s'" % pvname)
            return fields
        
        prefix = pvname
        isMotor = False
        if pvname.endswith('.VAL'):
            prefix = pvname[:-4]

        p = epics.PV(pvname)
        p.wait_for_connection(timeout=0.1)
        print 'Master Add PVname = ', pvname, p.connected
        if p.connected: 
            self.request_pv_cache(pvname)
            if ('.' not in prefix and p.type == 'double'):
                rtype = epics.PV(prefix+'.RTYP')
                rtype.wait_for_connection(0.1)
                if rtype is not None:
                    isMotor = 'motor' == rtype.get()
            if isMotor:
                fields = tuple(["%s%s" % (prefix,i) for i in motor_fields])
                pvs = []
                for pvname in fields:
                    pvs.append(epics.PV(pvname))

                epics.poll()
                for p in pvs:
                    p.wait_for_connection(timeout=0.5)
                    if p.connected:
                        self.request_pv_cache(p.pvname)
                    
        if isMotor and set_motor_pairs:
            time.sleep(0.25)
            self.set_allpairs(fields)
            
        return fields
コード例 #14
0
ファイル: Archiver.py プロジェクト: jdigilio/epicsarchiver
    def sync_with_cache(self,update_vals=False):
        """ initialize pv lists, insert times, etc with cache
        use update_vals to force insert of cache values into
        the archive (as on startup)
n       """
        newpvs = []
        cache_values = []
        self.get_cache_names()
        pvtable_data = self.pv_table.select()
        print ' This is sync with cache ', update_vals, len(self.cache_names), len(pvtable_data)
        self.db.use(self.master_db)
        now = time.time()
        print 'masterdb %s / data=%s' % ( self.master_db, len(pvtable_data))
        if update_vals:
            x = self.db.exec_fetch("select pvname,value,ts from cache")
            current_cache = {}
            for i in x:
                current_cache[i['pvname']] = i
                
        for pvdata in pvtable_data:
            name = normalize_pvname(pvdata['name'])
            
            if name not in self.cache_names:
                newpvs.append((name, epics.PV(name)))
            elif update_vals:  
                r = current_cache.get(name,None)
                if r is not None:
                    ts = r['ts']
                    if now - ts > SEC_DAY: ts = now
                    cache_values.append((name,ts,r['value']))

        if len(newpvs)>0:
            epics.poll()
            m = MasterDB()
            for pvname, pv in newpvs:
                if pv.connected:
                    m.add_pv(pvname)
            m.close()
        # now switch to archiving database, and (perhaps) insert values from cache
        if self.dbname is not None:
            self.db.use(self.dbname)
            for name,ts,value in cache_values:
                self.update_value(name,ts,value)
        print 'Sync with Cache Done'
コード例 #15
0
ファイル: MasterDB.py プロジェクト: kmpeters/epicsarchiver
 def get_related_pvs(self,pv,minscore=1):
     """return a list of related pvs to the provided pv
     with a minumum pair score"""
    
     out = []
     tmp = []
     npv = normalize_pvname(pv)
     if len(self.pvnames)== 0: self.get_pvnames()
     if npv not in self.pvnames: return out
     for i in ('pv1','pv2'):
         where = "%s='%s' and score>=%i order by score" 
         for j in self.pairs.select(where = where % (i,npv,minscore)):
             tmp.append((j['score'],j['pv1'],j['pv2']))
     tmp.sort()
     for r in tmp:
         if   r[1] == npv:  out.append(r[2])
         elif r[2] == npv:  out.append(r[1])
     out.reverse()
     return out
コード例 #16
0
ファイル: Instruments.py プロジェクト: jdigilio/epicsarchiver
    def set_instrument_pvs(self,pvlist,name=None,station=None,notes=''):
        insts = self.get_instruments(name=name,station=station)
        if len(insts)> 1:
            print 'multiple instruments matched name/station=%s/%s' % (name,station)
            return None
        if not isinstance(pvlist,(tuple,list)):
            print 'must provide list of PVs for instrument %s' % (name)
            return None
        inst_id = insts[0]['id']

        self.get_pvnames()
        for pvname in pvlist:
            pvn = normalize_pvname(pvname)

            where = "inst=%i and pvname='%s'" % (inst_id,pvn)
            x = self.inst_pvs.select_one(where=where)
            if x == {}:
                x = self.inst_pvs.insert(pvname=pvn,inst=inst_id)
            if pvn not in self.pvnames:  self.add_pv(pvn)

        # set pair scores
        allpvs = self.get_instrument_pvs(name=name,station=station)
        if len(allpvs) > 1:
            self.set_allpairs(allpvs)
コード例 #17
0
ファイル: MasterDB.py プロジェクト: kmpeters/epicsarchiver
 def __get_pvpairs(self,pv1,pv2):
     "fix and sort 2 pvs for use in the pairs tables"
     p = [normalize_pvname(pv1),normalize_pvname(pv2)]
     p.sort()
     return tuple(p)
コード例 #18
0
ファイル: Archiver.py プロジェクト: jdigilio/epicsarchiver
    def get_data(self,pvname,t0,t1,with_current=None):
        "get data from database for a time range"
        if pvname is None: return []
        pvname = normalize_pvname(pvname)
        info = self.get_info(pvname)
        if info is None:
            return ([],'No PV named %s',pvname)
            
        #
        stat   = [info]
        dat    = []
        pvquery= "select data_table,id from pv where name ='%s'" % pvname
        fquery = 'select time,value from %s where pv_id=%i and time<=%f order by time desc limit 1'
        gquery = 'select time,value from %s where pv_id=%i and time>=%f order by time limit 1'       
        squery = 'select time,value from %s where pv_id=%i and time>=%f and time<=%f order by time'

        needs_firstpoint = True
        tnow = time.time()

        # make sure t0 and t1 are ordered
        if t0 > t1:   t0,t1 = t1,t0
        if t1-t0 < 1800.0: t0 = t1 - 1800.0 # look back at least an hour

        # determine if we should append the current (cached) value
        if with_current is None:
            add_current = abs(t1-tnow) < 1.5 * SEC_DAY
        else:
            add_current = with_current
            
        # print 'get data for ' ,pvname, t0,t1
        # print 'dbs: ', self.dbs_for_time(t0,t1)
        try:
            for db in self.dbs_for_time(t0,t1):
                self.db.use(db)
                stat.append(pvquery)
                r     = self.db.exec_fetchone(pvquery)
                try:
                    table = r['data_table']
                    pvid  = r['id']
                except KeyError:  # this db doesn't know about this PV -- maybe it's a recent addition?
                    continue
                stat.append((db,table, pvid))
                if needs_firstpoint:
                    q = fquery % (table,pvid,t0)
                    stat.append(q)
                    r = self.db.exec_fetchone(q)
                    try:
                        dat.append((r['time'],r['value']))
                        needs_firstpoint = False
                    except:
                        stat.append('no data before t0!')
                q = squery % (table,pvid,t0,t1)
                stat.append(q)
                for i in self.exec_fetch(q):
                    dat.append((i['time'],i['value']))
            # add value at time just after selected time range
            r = self.db.exec_fetchone(gquery % (table,pvid,t1))
            try:
                dat.append((r['time'],r['value']))
            except KeyError:
                pass
            # optionally, add current value
            if add_current: 
                stat.append('adding cached value')
                r= self.get_cache_full(pvname)
                stat.append(r)
                if r['value'] is not None:
                    dat.append((time.time(),r['value']))
        except:
            stat.append('Exception!')
        dat.sort()
        self.use_currentDB()
        return dat,stat
コード例 #19
0
ファイル: Instruments.py プロジェクト: jdigilio/epicsarchiver
 def show_for_pv(self,pvname):
     pvname = normalize_pvname(pvname)
     for i in self.alerts.select(where="pvname='%s'" % pvname):
         print i
コード例 #20
0
ファイル: Archiver.py プロジェクト: jdigilio/epicsarchiver
    def add_pv(self,name,description=None,graph={},deadtime=None,deadband=None):
        """add PV to the database: expected to take a while"""
        pvname = normalize_pvname(name)

        t0_start = time.time()
        if not valid_pvname(pvname):
            sys.stdout.write("## Archiver add_pv invalid pvname = '%s'" % pvname)
            return None

        if pvname in self.pvinfo:
            if 'yes' == self.pvinfo[pvname]['active']:
                self.write("PV %s is already in database.\n" % pvname)
            else:
                self.write("PV %s is in database, reactivating!\n" % pvname)                
                self.pvinfo[pvname]['active'] = 'yes'
            return None
        # create an Epics PV, check that it's valid

        try:
            pv = epics.PV(pvname)
            pv.connect()
            pv.get_ctrlvars()
            typ = pv.type
            count = pv.count
            prec  = pv.precision
            connected = pv.connected 
        except:
            typ= 'int'
            count = 1
            prec = None
            connected = False

        if not connected:
            self.write("cannot add PV '%s': not connected" % pvname)
            return None

        # determine type
        dtype = 'string'
        if (typ in ('int','long','short')): dtype = 'int'
        if (typ in ('enum',)):              dtype = 'enum'
        if (typ in ('double','float')):     dtype = 'double'
        
        # determine data table
        table = "pvdat%3.3i" % ((hash(pvname) % 128) + 1)
        
        # determine descrption (don't try too hard!)
        if description is None:
            if pvname.endswith('.VAL'):
                descpv  = "%s.DESC" % pvname[:-4] 
            else:
                descpv  = "%s.DESC" % pvname 
                for f in motor_fields:
                    if pvname.endswith(f):
                        descpv = None
                        
            if descpv is not None:
                try:
                    dp = epics.PV(descpv)
                    description = dp.get(as_string=True)
                ## dp.disconnect()
                except:
                    pass
        if description is None: description = ''

        # set graph default settings
        gr = {'high':'','low':'','type':'normal'}
        gr.update(graph)
        if dtype == 'enum':
            x = pv.get(as_string=True)
            gr['type'] = 'discrete'
            gr['low'] = 0
            gr['high'] =  len(pv.enum_strs)
        elif dtype == 'double':
            gr['type'] = 'normal'
            dx = description.lower()
            for i in ('cathode','pirani','pressure'):
                if dx.find(i) >= 0: 
                    gr['type'] = 'log'
        
        if (deadtime == None):
            deadtime = config.pv_deadtime_dble
            if dtype in ('enum','string'):   deadtime = config.pv_deadtime_enum
            if (gr['type'] == 'log'): deadtime = 5.0  # (pressures change very frequently)

        if (deadband == None):
            deadband = 1.e-5
            if (gr['type'] == 'log'): deadband = 1.e-4
            if prec is not None: deadband = 10**(-(prec+1))
            if dtype in ('enum','string'):     deadband =  0.5
            
        self.write('Archiver adding PV: %s, table: %s' % (pvname,table))
        
        self.pv_table.insert(name    = pvname,
                             type    = dtype,
                             description= description,
                             data_table = table,
                             deadtime   = deadtime,
                             deadband   = deadband,
                             graph_lo   = gr['low'],
                             graph_hi   = gr['high'],
                             graph_type = gr['type'])

        
        dat  = self.pv_table.select_where(name=pvname)[0]
        dat['force_time'] = get_force_update_time()
        dat['last_ts'] = 0
        dat['last_value']  = None
        self.pvinfo[pvname] = dat

        self.update_value(pvname,time.time(),pv.value)

        self.write(" time=%f\n" % (time.time() - t0_start))

        sys.stdout.flush()
        pv = None        
コード例 #21
0
    def modify_instrument(self,**kw):
        ' form to add an instrument'

        inst_id = 0
        mykw = {'station':'','instrument':'','submit':'', 'form_id':0,'inst_id':inst_id}
        mykw.update(kw)

        if mykw['inst_id'] != 0:
            inst_id = int( mykw['inst_id'] )
        else:
            tmp = int(mykw['form_id'].strip())
            if tmp != 0: inst_id = tmp

        if inst_id == 0 and mykw['instrument'] != '' and mykw['station'] != '':
            inst_id = self.arch.get_instrument_id(name=mykw['instrument'],
                                                 station=mykw['station'])

        instrument,station = self.arch.get_instrument_names_from_id(inst_id)

        self.starthtml()
        self.show_links(help='instruments',active_tab='Instruments')

        wr = self.write
        
        if mykw['submit'] != '':
            sname = clean_input(mykw['name']).strip()
            if sname != instrument and sname != '':
                self.arch.instruments.update(name=sname,where="id=%i" % inst_id)

            sdesc = clean_input(mykw['desc']).strip()            
            if sdesc != '':
                self.arch.instruments.update(notes=sdesc,where="id=%i" % inst_id)
            
            # add and remove pvs as directed
            pvs_add = []
            pvs_del = []
            for k,v in mykw.items():
                if 'remove' == v:
                    pvs_del.append(k)
                elif k.startswith('_addpv_') and len(v) >0:
                    pvs_add.append(clean_input(normalize_pvname(v)))
                    
            if len(pvs_del)>0:
                self.arch.remove_instrument_pvs(pvs_del, name=instrument, station=station)

            if len(pvs_add)>0:
                self.arch.set_instrument_pvs(pvs_add, name=instrument, station=station)                


        info   = self.arch.get_instruments(name=instrument,station=station)[0]
        pvlist = self.arch.get_instrument_pvs(name=instrument,station=station)
        pvlist.sort()        

        wr('<form action ="%s/modify_instrument" enctype="multipart/form-data"  method ="POST"><p>' % (mainpage))
        wr('<input type="hidden" name="form_id"  value="%s">' % inst_id)
        wr("<h3>Definition for Instrument: %s      &nbsp;  &nbsp; in Station %s</h3> " % (instrument,station))

        wr("""<table><tr><td> Instrument Name:</td><td><input type='text'   name='name'  value='%s' size=35></td></tr>
        <tr><td> Description:</td><td><input type='text'   name='desc'  value='%s' size=35></td></tr>
        <tr><td> &nbsp; </td><td></td></tr>
        <tr><td colspan=2> Current PVs in instrument: </td><td></td></tr>
        """ % (instrument,info['notes']))
        
        for pvn in pvlist:
            self.write("""<tr><td>%s</td><td>
            <input type='radio'                name='%s' value='remove'>remove 
            <input type='radio' checked='true' name='%s' value='keep'>keep</td></tr>""" % (pvn,pvn,pvn))
        
        wr("<tr><td colspan=2> <hr> </td></tr> <tr><td colspan=2> Add PVs </td></tr>")
        for i in range(4):
            self.write("""<tr><td> PV &nbsp;</td><td>
            <input type="text" name="_addpv_%i" value="" size=35></td></tr>""" % i)

        wr("""<tr><td><input type='submit' name='submit' value='Update Definition'></td>
        <td><a href='%s?station=%s&instrument=%s'>View Positions for %s</a></td>
        </tr></table>""" % (instpage,station,instrument,instrument))

        self.endhtml()
        return self.get_buffer()