def __init__(self, dbconn=None, **kw): HTMLWriter.__init__(self) self.arch = Archiver(dbconn=dbconn) self.arch.db.get_cursor() self.dbconn = self.arch.dbconn self._gp = None if HAS_GNUPLOT: self._gp = Gnuplot.Gnuplot() self.kw = {'text_pv':'', 'text_pv2':'', 'use_ylog':'', 'use_y2log': '', 'submit': '', 'time_ago': '1 day', 'ymin':'', 'ymax':'', 'y2min':'', 'y2max':'', 'date1': '', 'date2': ''} self.kw.update(kw) for i in ('use_ylog', 'use_y2log'): if self.kw[i] not in ('Yes', 'No'): self.kw[i] = 'Auto' if not self.file_pref.endswith('/'): self.file_pref = "%s/" % self.file_pref
#!/usr/bin/env python # reports motor settings now and at some earlier time from EpicsArchiver import Archiver from epics import caget import time tnow = time.time() then = tnow - 3600*8.0 # change to your motor prefix pv_base = '13BMD:m' arch = Archiver() suff ='VAL' print 'PV Old Val Current Val Description' for i in range(1,20): pvname = "%s%i.%s" % (pv_base,i+1,suff) val_now = arch.get_value_at_time(pvname, tnow)[1] val_then = arch.get_value_at_time(pvname, then)[1] desc = caget( "%s%i.DESC" % (pv_base,i+1)) print "%s %15s %15s %s" % (pvname, val_then, val_now,desc)
class PlotViewer(HTMLWriter): ago_times = ('15 minutes', '30 minutes', '1 hour', '2 hours', '3 hours', '6 hours','8 hours','12 hours', '1 day','2 days','3 days', '1 week', '2 weeks', '1 month') years = range(2001, time.localtime()[0]+1) months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] days = (31,28,31,30,31,30,31,31,30,31,30,31) minutes = ('00','05','10','15','20','25', '30','35','40','45','50','55') file_pref = config.data_dir link_pref = config.data_url gp_base = """set nokey set term png transparent medium xffffff x000000 xe8e8e8 x0000dd xdd0000 xdd00dd xf2f2f2 set timefmt "%Y%m%d %H%M%S" set xdata time set format x "%H:%M\\n%b-%d" set style line 1 lt 1 lw 3 set style line 2 lt 2 lw 3 set style line 3 lt 3 lw 3 set style line 4 lt 4 lw 1 set grid back ls 4 """ gp2_base = """set key set y2tics set ytics nomirror """ html_title = "Epics Archiver Data Viewer" def __init__(self, dbconn=None, **kw): HTMLWriter.__init__(self) self.arch = Archiver(dbconn=dbconn) self.arch.db.get_cursor() self.dbconn = self.arch.dbconn self._gp = None if HAS_GNUPLOT: self._gp = Gnuplot.Gnuplot() self.kw = {'text_pv':'', 'text_pv2':'', 'use_ylog':'', 'use_y2log': '', 'submit': '', 'time_ago': '1 day', 'ymin':'', 'ymax':'', 'y2min':'', 'y2max':'', 'date1': '', 'date2': ''} self.kw.update(kw) for i in ('use_ylog', 'use_y2log'): if self.kw[i] not in ('Yes', 'No'): self.kw[i] = 'Auto' if not self.file_pref.endswith('/'): self.file_pref = "%s/" % self.file_pref def gp(self,s): " simple wrapper around gnuplot " self.gpfile.write("%s\n" % s) if HAS_GNUPLOT: self._gp(s) def fix_gpfile(self,fname,pattern): " simple wrapper around gnuplot " self.gpfile.flush() self.gpfile.close() f = open(fname,'r') lines = f.readlines() f.close() f2 = open(fname,'w') for l in lines: f2.write( l.replace(pattern,'') ) f2.close() def in_database(self,pvname): if not pvname: return False if pvname == '': return False x = pvname if x.find('.') == -1: x = "%s.VAL" % x return x in self.arch.get_cache_names() def draw_form(self,pv1='',pv2='',time_ago='',date1='',date2='',**kw): action = self.kw['submit'] pvname1 = pv1 or '' pvname2 = pv2 or '' self.write("<table><tr valign='top'><td>") self.startform(action=plotpage,name='plot') # pv1 = self.argclean(pvname1, self.kw['text_pv']) # self.write("<h3>Epics PV Archive: %s</h3>" % (time.ctime())) self.starttable(ncol=6, border=0, cellpadding=1) # inptext = self.textinput self.addrow("PV 1", inptext(name='text_pv', value=pvname1),"", "PV 2", inptext(name='text_pv2',value=pvname2),"") self.addrow("PV (Y) range:", "%s:%s" % (inptext(name='ymin',value=self.kw['ymin'],size=12), inptext(name='ymax',value=self.kw['ymax'],size=12)), "", "%s:%s" % (inptext(name='y2min',value=self.kw['y2min'],size=12), inptext(name='y2max',value=self.kw['y2max'],size=12)) , spans=(1,2,1,2)) # s = [] for y in ('use_ylog','use_y2log'): t = [] for i in ('Yes','No','Auto'): checked = (i == self.kw[y]) t.append(self.radio(name=y, value=i, checked=checked, text=i)) s.append(' '.join(t)) self.addrow("Log Scale?",s[0],s[1],spans=(1,2,2)) # self.addrow('','') self.addrow(self.button(text='Time From Present'), self.select(id='time_ago',name='time_ago', default=self.kw['time_ago'], choices=self.ago_times), spans=(1,5)) # self.addrow('','') dval = [self.kw.get('date1',''),self.kw.get('date2','')] if dval[0] in (None,'None', ''): dval[0] = time_sec2str( time.time()-SEC_DAY) if dval[1] in (None,'None', ''): dval[1] = time_sec2str( time.time() ) dates = ("%s <button id='date1_trig'>...</button>" % (inptext(size=22,name='date1',value=dval[0])), "%s <button id='date2_trig'>...</button>" % (inptext(size=22,name='date2',value=dval[1]))) self.addrow(self.button(text='Date Range'),"From: %s To: %s" % dates, spans=(1,5)) self.addrow("<hr>", spans=(6,0)) self.endtable() self.write(jscal_get_2dates) # main (lefthand side) of page done, x = self.make_related_pvs_page(pv1,pvname2,submit=self.kw['submit'], time_ago=self.kw['time_ago'], date1=self.kw['date1'], date2=self.kw['date2']) self.draw_graph(pvname1=pv1,pvname2=pv2) self.write("</td><td> %s </td></tr></table></form>" % x) def get_related_pvs(self,pvname): tmp = [] npv = normalize_pvname(pvname) r1 = self.arch.read_master("select * from pairs where pv1='%s' and score>1 order by score" %npv) for j in r1: tmp.append((j['score'],j['pv1'],j['pv2'])) r2 = self.arch.read_master("select * from pairs where pv2='%s' and score>1 order by score" %npv) for j in r2: tmp.append((j['score'],j['pv1'],j['pv2'])) tmp.sort() out = [] for r in tmp: if r[1] == npv and r[2] not in out: out.append(r[2]) elif r[2] == npv and r[1] not in out: out.append(r[1]) out.reverse() return out 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) def increment_pair_score(self,pv1,pv2): # get current score: pvns = self.__get_pvpairs(pv1,pv2) where = "pv1='%s' and pv2='%s'" % pvns o = self.arch.read_master("select * from pairs where %s" % where) try: score = int(o[0]['score']) except: score = -1 if score < 1: q = "insert into pairs set score=%i, pv1='%s', pv2='%s'" else: q = "update pairs set score=%i where pv1='%s' and pv2='%s'" score = max(1, score+1) self.arch.read_master(q % (score,pvns[0],pvns[1])) def make_related_pvs_page(self,pvname,pvname2,submit='',time_ago='',date1='',date2=''): out = [] r = self.get_related_pvs(pvname) args = '' if submit.startswith('Time From') and time_ago != '': args = '&time_ago=%s' % (time_ago) elif submit.startswith('Date') and date1 != '' and date2 != '': args = '&date1=%s&date2=%s' % (date1,date2) out.append("<p class='xtitle'>Related pvs:%s<p><font size=-1>" % '') # pvname) if pvname2 != '': pvargs = 'pv2=%s&pv=%s' % (pvname,pvname2) out.append("<a href='%s?%s%s'>Swap PV1 and 2</a></p><hr>" % (plotpage,pvargs,args)) n = 0 for pv2 in r: pvargs = 'pv=%s&pv2=%s' % (pvname,pv2) out.append("<a href='%s?%s%s'>%s</a></p>" % (plotpage,pvargs,args,pv2)) n = n + 1 if n>20: break out.append('</font>') return '\n'.join(out) def draw_graph(self,pvname1='',pvname2='',time_ago=None): # self.show_keys(title='AT Draw Graph') if DEBUG: self.write(" GRAPH %s / %s " % (pvname1,pvname2)) self.write('<p> === Keys: === </p>') for key,val in self.kw.items(): self.write(" %s : %s <br>" % (key,val)) t1 = time.time() + 10.0 # now+10seconds (make sure we don't lose "now") t0 = t1 - SEC_DAY action = self.kw['submit'] if action.startswith('Time From'): n,units = self.kw['time_ago'].split() if units.startswith('mi'): mult = 60. elif units.startswith('ho'): mult = 3600. elif units.startswith('da'): mult = SEC_DAY elif units.startswith('we'): mult = SEC_DAY * 7 elif units.startswith('mo'): mult = SEC_DAY * 31. t0 = t1 - int(n) * mult else: dx = time.localtime() t0 = time_str2sec(self.kw['date1']) t1 = time_str2sec(self.kw['date2']) if t1 < t0: t1,t0 = t0,t1 # self.write('<p> draw graph %i %i </p>' % (t0,t1)) # froot = "%s%s" % (config.webfile_prefix,timehash()) f_png = os.path.join(self.file_pref, "%s.png" % froot) l_png = os.path.join(self.link_pref, "%s.png" % froot) f_gp = os.path.join(self.file_pref, "%s.gp" % froot) l_gp = os.path.join(self.link_pref, "%s.gp" % froot) f_dat = os.path.join(self.file_pref, "%s.dat" % froot) f_dat2 = os.path.join(self.file_pref, "%s_2.dat" % froot) l_dat = os.path.join(self.link_pref, "%s.dat" % froot) l_dat2 = os.path.join(self.link_pref, "%s_2.dat" % froot) self.gpfile = open(f_gp,'w') # get PV and related data epv1 = self.arch.get_pv(pvname1) pvinfo = self.arch.get_info(pvname1) pv2info = pvinfo ## self.write(" PV %s %s " % ( pvname1,epv1)) if epv1 is None or pvinfo=={}: return ('','') if (epv1.pvname in (None,'')): return ('','') pvlabel = pvname1 legend, tics = self.get_enum_legend(epv1) desc = pvinfo.get('description','') if desc in ('',None): desc = self.get_pvdesc(epv1) if desc != epv1.pvname: pvlabel = "%s (%s)" % (desc,epv1.pvname) file_link = "<a href='%s'>data for %s</a>" % (l_dat,pvlabel) tlo, thi,npts,dat = self.save_data(epv1,t0,t1,f_dat,legend) if npts < 1: self.write("<br>Warning: No data for PV %s <br>" % (epv1)) wait_for_pngfile = npts > 1 npts2 = 0 n_dat = 1 # start gnuplot session, set basic properties self.gp(self.gp_base) # are we plotting a second data set? if DEBUG: self.write("<br> pv2??? %s, %s <br>" % (pvname2, str(pvname2==''))) if pvname2 != '': epv2 = self.arch.get_pv(pvname2) epv2.connect() pv2info = self.arch.get_info(pvname2) self.increment_pair_score(pvname1,pvname2) if DEBUG: self.write(" PV#2 !!! %s, %s" % (str(pvname2 is None), epv2.pvname)) if epv2 is not None and epv2.pvname != '': val = epv2.get() leg2, tics2 = self.get_enum_legend(epv2) desc2 = pv2info.get('description','') if desc2 in ('',None): desc2 = self.get_pvdesc(epv2) pv2label = pvname2 if desc2 != pvname2: pv2label = "%s (%s)" % (desc2, pvname2) file_link ="""<a href='%s'>data for %s</a><br> <a href='%s'>data for %s</a>""" % (l_dat,pvlabel,l_dat2,pv2label) tlo2, thi2, npts2,dat2 = self.save_data(epv2,t0,t1,f_dat2,leg2) if npts2 < 1: self.write("<br>Warning: No data for PV %s <br>" % (pv2)) tlo = min(tlo2, tlo) thi = max(thi2, thi)+1 n_dat = 2 self.gp(self.gp2_base) wait_for_pngfile = wait_for_pngfile and (npts2 > 1) if DEBUG: self.write(" # of data_sets %i" % n_dat) # now generate png plot self.gp("set output '%s'" % f_png) # if npts > 1 or npts2>1: # self.gp('set xrange ["%s":"%s"]' % (self.datestring(t0),self.datestring(t1))) # else: self.gp('set xrange ["%s":"%s"]' % (self.datestring(t0),self.datestring(t1))) if pvinfo['type']=='double': ymin = str(pvinfo['graph_lo']) or '' if self.kw['ymin'] != '': ymin = self.kw['ymin'] ymax = str(pvinfo['graph_hi']) or '' if self.kw['ymax'] != '': ymax = self.kw['ymax'] self.gp("set yrange [%s:%s]" % (ymin,ymax)) use_ylog = self.kw['use_ylog'] if use_ylog == 'Auto' and pvinfo['type']=='double': if pvinfo['graph_type']=='log': use_ylog ='Yes' if use_ylog=='Yes': self.gp("set zero 1e-14") self.gp("set logscale y") if n_dat==2 and pv2info['type']=='double': y2min = str(pv2info['graph_lo']) or '' if self.kw['y2min'] != '': y2min = self.kw['y2min'] y2max = str(pv2info['graph_hi']) or '' if self.kw['y2max'] != '': y2max = self.kw['y2max'] self.gp("set y2range [%s:%s]" % (y2min,y2max)) use_y2log = self.kw['use_y2log'] if use_y2log == 'Auto' and pv2info['type']=='double': if pv2info['graph_type']=='log': use_y2log ='Yes' if use_y2log=='Yes': self.gp("set zero 1e-14") self.gp("set logscale y2") if epv1.type =='enum': self.gp("set ytics %s" % tics) try: n_enum = len(epv1.enum_strs) except: n_enum = 8 self.gp("set yrange [-0.2:%f]" % (n_enum-0.8)) if n_dat==2 and epv2.type =='enum': self.gp("set y2tics %s" % tics2) try: n_enum = len(epv2.enum_strs) except: n_enum = 8 self.gp("set y2range [-0.2:%f]" % (n_enum-0.8)) if n_dat == 1: self.gp("set title '%s'" % (desc)) self.gp("set ylabel '%s'" % (pvlabel)) self.gp("""plot '%s' u 1:4 w steps ls 1 t '%s', \\ '%s' u 1:4 t '' w p 1 """ % (f_dat,desc,f_dat)) else: self.gp("set title '%s | %s'" % (desc,desc2)) self.gp("set ylabel '%s'" % (pvlabel)) self.gp("set y2label '%s'" % (pv2label)) self.gp("""plot '%s' u 1:4 axis x1y1 w steps ls 1 t '%s',\\ '%s' u 1:4 axis x1y1 t '' w p 1,\\ '%s' u 1:4 axis x1y2 w steps ls 2 t '%s',\\ '%s' u 1:4 axis x1y2 t '' w p 2 """ % (f_dat,desc,f_dat,f_dat2,desc2,f_dat2)) self.arch.use_currentDB() wait_count = 0 png_size = 0 while wait_for_pngfile: try: png_size = os.stat(f_png)[6] except OSError: pass wait_for_pngfile = (png_size < 4) and (wait_count < 500) time.sleep(0.005) wait_count = wait_count + 1 self.fix_gpfile(f_gp, self.file_pref) # self.write("<b> fix %s, %s / wait_count = %i</b> " % (f_gp, self.file_pref,wait_count)) if png_size > 0: self.write("<img src='%s'><br>" % l_png) else: self.write("<p>Cannot make requested graph!<p>") self.write("<br>Data for <b>%s</b>:<br>" % epv1.pvname) self.write("<table border=1 padding=1><tr><td>Date</td><td>Value</td></tr>") for d in dat: self.write("<tr><td>%s</td><td>%s</td></tr>" % (time.ctime(d[0]),d[1])) self.write("</table>") if n_dat == 2: self.write("<p>Data for <b>%s</b>:<br>" % epv2.pvname) self.write("<table border=1 padding=1><tr><td>Date</td><td>Value</td></tr>") for d in dat2: self.write("<tr><td>%s</td><td>%s</td></tr>" % (time.ctime(d[0]),d[1])) self.write("</table>") self.write("<br>%s<br>" % (file_link)) self.write("<a href='%s'>gnuplot script</a> <br>" % (l_gp)) return def show_keys(self,**kw): for k,v in kw.items(): self.write("%s : %s<br>" % (k,v)) def get_pvdesc(self,pv): try: xpv = pv.pvname if xpv.endswith('.VAL'): xpv = xpv[:-4] desc = epics.caget("%s.DESC" % xpv) except: desc = None if desc in ('',None,' '): desc = pv.pvname if not isinstance(desc, str): desc = pv.pvname try: desc = desc.replace('"','_') desc = desc.replace("'",'_') except: pass return desc def get_enum_legend(self, pv): # self.write(" <BR> PV ENUM STR For %s<BR>" % repr(pv).replace('<','|') ) legend, tics = '', '' if not pv.connected: pv.wait_for_connection(timeout=0.5) if pv.type == 'enum': xtmp = pv.get(as_string=True) legend = " Legend: [" tics = '' if pv.enum_strs is None: x = pv.get_ctrlvars() if pv.enum_strs is None: t0 = time.time() while pv.enum_strs is None and time.time()-t0 < 3.0: x = pv.get(as_string=True) try: for i, nam in enumerate(pv.enum_strs): legend = "%s %i= %s |" % (legend, i, nam) tics = '%s "%s" %i,' % (tics, nam, i) legend = "%s ]" % legend[:-1] tics = "(%s)" % tics[:-1] except: legend = '' tics = '' return (legend,tics) def datestring(self,t): "return 'standard' date string given a unix timestamp" # Gnuplot hint: set xrange ["2005/08/17 09:00:00":"2005/08/17 14:00:00"] return time.strftime("%Y%m%d %H%M%S", time.localtime(t)) def save_data(self,pv,t0,t1,fout,legend): "get data from database, save into tmp gnuplot-friendly file" dat,stat = self.arch.get_data(pv.pvname,t0,t1) pvname = pv.pvname if DEBUG: self.write("<p>DATA for PV %s (%i:%i) #points =%i<br>\n" % (pvname,t0,t1,len(dat))) for i in stat: self.write("%s<br>\n" % str(i)) npts = len(dat) errstring = '' try: tlo = dat[0][0] thi = dat[npts-1][0] except IndexError: dat = [(t0,None),(t1,None)] npts,tlo,thi = 0,t0,t1 errstring = 'No data for %s' % pv.pvname # now write data to gnuplot-friendly file dstr = self.datestring f = open(fout, 'w') f.write("# %s (%s)\n" % (pv.pvname,pv.type)) f.write("# requested time_span: [%s , %s] \n" % (dstr(t0),dstr(t1))) f.write("# actual time_span: [%s , %s] \n" % (dstr(tlo),dstr(thi))) if legend != '': f.write("# %s \n" % legend) if errstring != '': f.write("# ERROR: %s \n" % legend) else: f.write("# n_points: %i \n" % npts) f.write("#-------------------------------\n") f.write("# date time value\n") for j in dat: val = j[1] if isinstance(val, str): try: val = float(val) except: pass f.write("%s %.3f %s\n" % (dstr(j[0]), j[0], j[1])) f.close() return (tlo, thi, npts,dat) def argclean(self,argval,formval): v = formval.strip() if v != '': return v a = argval if a is None: a = '' if not isinstance(a,str): a = str(a) return a.strip() def OLD_show_plot(self,pv=None,pv2=None,time_ago=None,date1=None,date2=None,**kw): self.kw.update(kw) pv1 = self.argclean(pv, self.kw['text_pv']) pv2 = self.argclean(pv2, self.kw['text_pv2']) self.arch.db.get_cursor() if pv1 != '': pv1 = normalize_pvname(pv1) self.html_title = "%s for %s" % (self.html_title,pv1) if not self.in_database(pv1): self.arch.add_pv(pv1) if pv2 != '': pv2 = normalize_pvname(pv2) self.html_title = "%s and %s" % (self.html_title,pv2) if not self.in_database(pv2): self.arch.add_pv(pv2) # self.starthtml() self.show_links(pv=pv1,help='plotting',active_tab='') self.draw_form(pv1=pv1,pv2=pv2,time_ago=time_ago,date1=None,date2=None) self.endhtml() self.arch.db.put_cursor() return self.get_buffer() def update_kw(self,key,arg=None): if key is None: return arg if key not in self.kw: self.kw[key] = '' if arg is None: arg = '' if not isinstance(arg,str): arg = str(arg) if arg != '': self.kw[key] = arg.strip() return arg.strip() def do_plot(self,pv='',pv2='',time_ago=None,date1=None,date2=None,**kw): self.kw.update(kw) self.update_kw('pv',pv) self.update_kw('pv2',pv2) self.update_kw('time_ago',time_ago) self.update_kw('date1',date1) self.update_kw('date2',date2) self.starthtml() if self.kw.get('text_pv','') not in ('',None): self.kw['pv'] = self.kw['text_pv'] if self.kw.get('text_pv2','') not in ('',None): self.kw['pv2'] = self.kw['text_pv2'] pv1 = self.kw.get('pv','') pv2 = self.kw.get('pv2','') action = self.kw.get('submit','') if action == '': action = 'Time From Present' if (self.kw.get('date1','') != '' and self.kw.get('date2','') != ''): action = 'Date Range' if self.kw.get('submit','') == '': self.kw['submit'] = action self.arch.db.get_cursor() warnings = [] if pv1 != '': pv1 = normalize_pvname(pv1) self.html_title = "%s for %s" % (self.html_title,pv1) if not self.in_database(pv1): add_ok = self.arch.add_pv(pv1) if add_ok is None: warnings.append(" Warning: cannot add PV '%s'<br>" % pv1 ) if pv2 != '': pv2 = normalize_pvname(pv2) self.html_title = "%s and %s" % (self.html_title,pv2) if not self.in_database(pv2): add_ok = self.arch.add_pv(pv2) if add_ok is None: warnings.append(" Warning: cannot add PV '%s'<br>" % pv2 ) self.show_links(pv=pv1,help='plotting',active_tab='') for w in warnings: self.write(w) # self.show_keys(title='AT DO PLOT') self.draw_form(pv1=pv1,pv2=pv2,time_ago=time_ago,date1=date1,date2=date2) self.endhtml() self.arch.db.put_cursor() return self.get_buffer() def show_keys(self,title=''): self.write('===== %s Keys: === </p>' % title) for key,val in self.kw.items(): self.write(" %s : '%s' <br>" % (key,val)) self.write('=====')