From 49dc30ce17211f21259c5bdb36f503f4e992e2c7 Mon Sep 17 00:00:00 2001 From: x03daop Date: Mon, 2 Nov 2015 08:59:14 +0100 Subject: [PATCH] Startup --- script/otf_thread.py | 701 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 701 insertions(+) create mode 100644 script/otf_thread.py diff --git a/script/otf_thread.py b/script/otf_thread.py new file mode 100644 index 00000000..a993300b --- /dev/null +++ b/script/otf_thread.py @@ -0,0 +1,701 @@ +""" +Class library for the Pearl PGM + +OTF scans of monochromator angles + +Author: + Juraj Krempasky + Matthias Muntwiler + +Created: + Feb 19, 2013 +""" +import time, os, os.path, sys, string, re +ARCH = os.environ['EPICS_HOST_ARCH'] +sys.path.insert(0, '/afs/psi.ch/user/w/wang_x1/public/'+ARCH) +sys.path.append('/afs/psi.ch/user/k/krempasky/public') +from jkpkg.app import epicsMotor +from jkpkg.app import epv +import threading +import blstatus + +MAXARRAY = 6000 + +#!OTF +class OTFscan_th(threading.Thread): + def __init__(self, worker): + threading.Thread.__init__(self) + self.worker = worker + self.running = 1 # assign 0 to end the thread + self.start() + + def run(self): + try: + #self.worker.eopt = 0 # do not drive PGM+ID + parm = {'FOLDER':self.worker.folder, 'FILE':self.worker.file} + (isok,rstring) = self.worker.makefile(parm, set=1, xmcd=self.worker.xmcd) + if isok: + self.worker.msg = "%s"%(os.path.basename(rstring)) + self.worker.path = rstring + else: + self.worker.msg = "%s"%(rstring) + raise Exception, self.worker.msg + + print self.worker.msg + self.worker.outparam['done']=0 + uprate = self.worker.uparam['uprate'] + #(E1,E2,tr_time)=self.worker.uparam['otf'] + #(beta1, beta2, theta1, theta2, ttime) = self.uparam['angles'] + print "update interval is %g seconds" % (uprate * 0.01) + cnt = 0 + + while self.running: + time.sleep(0.01) + if cnt >= uprate: + cnt = 0 + else: + cnt = cnt + 1 + continue + + try: + self.worker.getdata() + except: + self.worker.msg = "EPICS channels missing" + break + if not self.running: + break + if not self.worker.isbusy(): + break + E_act = self.worker.E_rbk + #self.worker.outparam['pb_set']=int(100*(E_act-E1)/(E2-E1)) + self.worker.outparam['Erbk']=("%.1f eV"%(E_act)) + + # do not overshoot + if self.worker.check_endpoint(): + self.worker.stop() + break + if self.worker.pntcnt > MAXARRAY: + self.worker.msg = "OTF points exceed %d, abort..."%(MAXARRAY) + raise Exception, self.worker.msg + + self.worker.msg = self.worker.msg + ' done' + print self.worker.msg + self.worker.savedata(self.worker.path, self.worker.file) + finally: + print "scan thread exit" + self.worker.stop() + self.worker.outparam['done']=1 + self.worker.running = 0 + + +##!1################################################################### +class epicsPGM(object): + """ + detector = "kei6517", "ah401", or "cadc" (default) + """ + def __init__(self, detector = "cadc"): + # keep the identifiers consistent with the region.py module + self.err = None + self.pvs = {} #to monitor + self.xpvs = {} #to monitor + self.pvsdata = {} + self.setpvs = {} + self.mm = epicsMotor.motor("X03DA-PGM:MI") + self.gm = epicsMotor.motor("X03DA-PGM:GR") + self.detector = detector + + #uparam['cadc_otf']= sclock, sbuffer, srate + #sclock: 0:1kHz 1:5kHz 2:10kHz 3:50kHz 4:100kHz + #srate: Idle 1 10 100 Hz + + self.uparam={'uprate':10,\ + 'ptpsp':[(560,600,10),(600,615,5)],\ + 'otf':[840.0, 880.0, 1.0],\ + 'angles':[-87.0, -87.0, 86.0, 86.0, 1.0],\ + 'cadc_otf':[4,600,3],\ + 'cadc_ptp':[2,10000,0]} + #'cadc_otf':[3,1000,3],'cadc_ptp':[1,10000,0]} + + self.outparam={'pb_len':0,'pb_set':0,'Erbk':0,'done':0} + self.running = 0 + self.xmcd = None + self.xmcdrunning = 0 + self.pntcnt = 0 + self.cmd = '???' + self.msg = 'X03DA OTF (%s)' % (self.detector) + self.folder = 'TEST' + self.file = 'test' + self.format = 'itx' + self.path = "/tmp/otfdata" + '.' + self.format + self.connect() + + def connect(self): + self.err = None + if 1: + # PVs read via getdata() + self.pvs['E'] = epv.epv('X03DA-PGM:energy') + self.pvs['E_rbk'] = epv.epv('X03DA-PGM:rbkenergy') + self.pvs['E_crbk'] = epv.epv('X03DA-PGM:CERBK') + self.pvs['cff_rbk'] = epv.epv('X03DA-PGM:rbkcff') + self.pvs['theta'] = epv.epv('X03DA-PGM:theta') + self.pvs['theta_rbk'] = epv.epv('X03DA-PGM:rbktheta') + self.pvs['beta'] = epv.epv('X03DA-PGM:beta') + self.pvs['beta_rbk'] = epv.epv('X03DA-PGM:rbkbeta') + self.pvs['grating'] = epv.epv('X03DA-PGM:grating') + self.pvs['dmov'] = epv.epv('X03DA-PGM:dmov') + if self.detector == 'kei6517': + self.pvs['K1'] = epv.epv('X03DA-KEI11:READOUT') + self.pvs['K2'] = epv.epv('X03DA-KEI12:READOUT') + elif self.detector == 'ah401': + self.pvs['K1'] = epv.epv('X03DA-AH401B:CH1') + self.pvs['K2'] = epv.epv('X03DA-AH401B:CH2') + elif self.detector == 'cadc3': + self.pvs['K1'] = epv.epv('X03DA-OP:CURRENT3') + self.pvs['K2'] = epv.epv('X03DA-OP:CURRENT2') + else: # cadc + self.pvs['K1'] = epv.epv('X03DA-OP:CURRENT1') + self.pvs['K2'] = epv.epv('X03DA-OP:CURRENT2') + + print "selected detector: " + self.detector + + self.pvs['machine current (mA)'] = epv.epv('ARIDI-PCT:CURRENT') + self.xpvs['HALT'] = epv.epv('X03DA-PGM:stop') + self.xpvs['alldone'] = epv.epv('X03DA-PGM:dmov') + + # set PVs + self.setpvs['set_E'] = epv.epv('X03DA-PGM:setE.PROC') + self.setpvs['set_cff'] = epv.epv('X03DA-PGM:dftcff.C') + self.setpvs['set_beta'] = epv.epv('X03DA-PGM:ronbeta.A ') + self.setpvs['set_theta'] = epv.epv('X03DA-PGM:rontheta.A ') + #self.pvs[E].pend_io() + + self.ResetPvsData() + + # Wait for all PVs to connect + try: + self.pvs['E'].pend_io() + except: + self.err = 'EPICS channels not available' + print ("connect() ERROR:%s"%(self.err)) + return 0 + return 1 + + def addIgorWave(self, id, wave, figor, unit='', phystype='', axiswave=''): + """ + adds a one-dimensional dataset to the open igor file. + id = key of the data source in the pvsdata dictionary + wave = target name of the igor wave + figor = file object with the open igor file + unit = physical unit. is overridden by EPICS EGU field if available + phystype = physical data type (see pearl-optics-import-data.itx) + axiswave = wave name of independent axis + """ + if not id in self.pvsdata.keys(): + print "id. %s unknown\n"%(id) + return + vec = self.pvsdata[id] + # wave header and data + if id in self.pvs.keys(): + pvname = self.pvs[id].pvname + try: + unit = self.pvs[id].callBack.pv_units + except: + pass + else: + pvname = '' + if pvname: + line = 'X // %s\n' % (pvname) + figor.write(line) + line = 'WAVES/D/O/N=(%d) %s\nBEGIN\n' % (len(vec),wave) + figor.write(line) + svec = string.replace(str(vec),',',' ') + figor.write(svec[1:len(svec)-1]+'\n') + figor.write('END\n') + # scaling and meta-data + if unit: + line = 'X setscale d 0,0,"%s",%s\n' % (unit, wave) + figor.write(line) + if pvname: + line = 'X note %s, "PV=%s"\n' % (wave, pvname) + figor.write(line) + if phystype: + line = 'X note %s, "PhysicalType=%s"\n' % (wave, phystype) + figor.write(line) + if axiswave: + line = 'X note %s, "Axis1=%s"\n' % (wave, axiswave) + figor.write(line) + figor.write('\n') + + def savedata_itx(self, path='/tmp/otfdata.itx', comment=None): + """ + saves measured data in an igor text file. + path = file name including path. + the file name will be used as igor data folder name. + it should not be longer than 32 characters + and not contain too many special characters. + non-alphanumeric characters are changed to underscores for folder names. + """ + try: + fo = open(path,'w') + except: + print 'savedata_itx(): cannot open %s\n' % (path,) + return + + line = 'IGOR\n' + fo.write(line) + line = 'X // X03DA OTF scan version 2013-04-02\n' + fo.write(line) + (datafolder, ext) = os.path.splitext(os.path.basename(path)) + datafolder = re.sub('[^a-zA-Z0-9_]', '_', datafolder, count=0) + if datafolder: + line = '\nX newdatafolder /s/o root:raw' + #fo.write(line) + line = '\nX newdatafolder /s/o %s\n\n' % (datafolder) + #fo.write(line) + if comment: + line = 'X string comment = "%s"\n\n' % (comment) + + # wave names should conform to the list in pearl-optics-import-data.itx + self.addIgorWave('elapsed_time_secs', 'elapsed_time', fo, \ + phystype='time', unit='s') + self.addIgorWave('machine current (mA)', 'ringcurrent', fo, \ + phystype='ring current', unit='mA', axiswave='photonenergy') + self.addIgorWave('E_crbk', 'photonenergy', fo, \ + phystype='photon energy', unit='eV', axiswave='elapsed_time') + self.addIgorWave('cff_rbk', 'cff', fo, \ + phystype='focus constant', unit='', axiswave='photonenergy') + if self.scanType == "angle": + self.addIgorWave('beta_rbk', 'beta_rbk', fo, \ + phystype='grating angle', unit='deg', axiswave='photonenergy') + self.addIgorWave('theta_rbk', 'theta_rbk', fo, \ + phystype='mirror angle', unit='deg', axiswave='photonenergy') + # write data if channel is available + if self.pvs['K1'].getw() <> 0.0: + self.addIgorWave('K1', 'current_ch1', fo, \ + phystype='current', unit='A', axiswave='photonenergy') + line = 'X current_ch1 *= 400/ringcurrent\n' + fo.write(line) + line = 'X note current_ch1, "normalized to ringcurrent"\n\n' + fo.write(line) + if self.pvs['K2'].getw() <> 0.0: + self.addIgorWave('K2', 'current_ch2', fo, \ + phystype='current', unit='A', axiswave='photonenergy') + line = 'X current_ch2 *= 400/ringcurrent\n' + fo.write(line) + line = 'X note current_ch2, "normalized to ringcurrent"\n\n' + fo.write(line) + + bls = blstatus.BLStatus() + bls.save_itx_waves(fo) + + fo.close() + + + def makefile(self, parm, set = 0, xmcd=None): + ''' + parm: string dictionary to be evaluated + set: create the file + ''' + set = set + keys=['FOLDER','FILE'] + try: + parm = eval(str(parm)) + except: + return (0,"ERROR:%s format"%(str(parm))) + if type(parm)<>type({}): + return (0,"ERROR:%s not dict"%(str(parm))) + if len(parm.keys())<>2: + return (0,"ERROR:%s 2 keys needed"%(str(parm))) + + if 'FOLDER' not in parm.keys(): + return (0,"ERROR: FOLDER key mising") + if 'FILE' not in parm.keys(): + return (0,"ERROR: FILE key mising") + + upath = os.getenv('HOME')+'/Data1/' + folder = parm['FOLDER'] + folder = os.path.join(upath, folder) + + if not os.path.exists(folder): + try: + os.makedirs(folder) + except: + return (0,'ERROR mkdir %s'%(folder)) + + tstamp = time.strftime("%y%m%d-%H%M%S", time.localtime()) + fname = "otf-%s.%s" % (tstamp, self.format) + path = os.path.join(folder, fname) + if set: + try: + fd = open(path,'w') + fd.close() + except: + msg = '%s %s'%(sys.exc_info()[0],sys.exc_info()[1]) + return (0,msg) + + return (1,path) + + def savedata(self, path='/tmp/otfdata.txt', comment=None): + if self.format == 'itx': + self.savedata_itx(path, comment) + return + + try: + fd=open(path,'w') + except: + print 'savedata(): cannot open %s\n'%(path,) + return + #Frithjof wants this order: + keys=['E_crbk','K1','K2','K3','K4','machine current (mA)','cff_rbk','elapsed_time_secs'] + #keys = self.pvs.keys() + header=str(keys)[1:-1]; + header = string.replace(header,"elapsed_time_secs",'time') + header = string.replace(header,',',' ') + #header = string.replace(header,' ','') + header = string.replace(header,"'",'') + header = string.replace(header,",",' ') + header = string.replace(header,"_",'') + header = string.replace(header,"machine current (mA)",'MCurr') + fd.write(header+'\n') + + vec = self.pvsdata[keys[0]] + cnt = len(vec)-1 + print '%s data length %d\n'%(keys[0],cnt,) + + for i in range(cnt): + txt = '' + for key in keys: + #print ('%s %s\n')%(i,key) + vec = self.pvsdata[key] + #print vec + s = str(vec[i]) + #s = str(dili[i].values())[1:-1];string.replace(s,',',' ') + txt=txt+s+ ' ' + txt=txt+'\n' + fd.write(txt) + + fd.close() + + def ResetPvsData(self): + self.start = time.time() + self.pvsdata['timestamp'] = [0] + self.pvsdata['elapsed_time_secs'] = [0] + + self.pvsdata['machine current (mA)'] = [0] + self.pvsdata['E'] = [0] + self.pvsdata['E_rbk'] = [0] + self.pvsdata['E_crbk'] = [0] + self.pvsdata['cff'] = [0] + self.pvsdata['cff_rbk'] = [0] + self.pvsdata['theta'] = [0] + self.pvsdata['theta_rbk'] = [0] + self.pvsdata['theta_set'] = [0] + self.pvsdata['beta'] = [0] + self.pvsdata['beta_rbk'] = [0] + self.pvsdata['beta_set'] = [0] + self.pvsdata['grating'] = [0] + self.pvsdata['dmov'] = [0] + self.pvsdata['K1'] = [0] + self.pvsdata['K2'] = [0] + + +#!setE + def setE(self,E,fast=0, mode=None, id=None): + ''' mode: 0:LINEAR 1:CIRC+, 2:CIRC-''' + if fast: + self.velo() + #if self.E == E: + # return + + self.E = E + self.set_E = 1 + + def velo(self, maxspeed = 0.03): + ''' + set velocities to custom or maximum values + ''' + if maxspeed > 0.03: + maxspeed = 0.03 + (self.mm.VELO, self.gm.VELO) = (maxspeed, maxspeed) + time.sleep(0.05) + #print "mirror: vbas=%f velo=%f acc=%f"%(self.mm.VELO, self.mm.VBAS, self.mm.ACCL) + #print "grating: vbas=%f velo=%f acc=%f"%(self.gm.VELO, self.gm.VBAS, self.gm.ACCL) + + def setDft(self): + ''' + reset velocities to default values + ''' + (self.mm.VELO, self.gm.VELO) = (0.03, 0.03) + (self.mm.VBAS, self.gm.VBAS) = (0.003, 0.003) + + def set_EnergyScan(self, E1, E2, tr_time = 2): + ''' + sets up an energy scan + E1 = start energy in eV + E2 = end energy in eV + tr_time = travel time in minutes + call start_scan() to actuall start the scan + ''' + self.travel_time = tr_time + self.E = E1 + time.sleep(0.05) + (self.E1, self.theta1, self.beta1) = (self.E, self.theta, self.beta) + self.E = E2 + time.sleep(0.05) + (self.E2, self.theta2, self.beta2) = (self.E, self.theta, self.beta) + self.scanType = 'energy' + print "Start: E = %f, theta = %f, beta = %f" % (self.E1, self.theta1, self.beta1) + print "End: E = %f, theta = %f, beta = %f\n" % (self.E2, self.theta2, self.beta2) + + def set_AngleScan(self, beta1, beta2, theta1, theta2, tr_time = 2): + ''' + sets up a linear angle scan + beta1 = start angle in degrees + beta2 = end angle in degrees + theta1 = start angle in degrees + theta2 = end angle in degrees + tr_time is travel time in minutes + call start_scan() to actuall start the scan + ''' + self.travel_time = tr_time + self.theta1 = theta1 + self.theta2 = theta2 + self.beta1 = beta1 + self.beta2 = beta2 + self.scanType = 'angle' + print "Start: theta = %f, beta = %f" % (self.theta1, self.beta1) + print "End: theta = %f, beta = %f\n" % (self.theta2, self.beta2) + + def start_scan(self): + ''' + starts an on-the-fly scan + which was set up by one of the set_XxxxScan() functions + ''' + + self.msg = "moving to start..." + print self.msg + mbeta = abs(self.beta1 - self.beta_rbk) > 0.0001 + mtheta = abs(self.theta1 - self.theta_rbk) > 0.0001 + # note: do not read back from self.beta or self.theta! + if mbeta: + self.set_beta = self.beta1 + if mtheta: + self.set_theta = self.theta1 + nretries = 5 + while (mbeta or mtheta) and (nretries > 0): + nretries = nretries - 1 + if self.waitdone(timeout = 30 / 0.1): + time.sleep(2) + print "Readback: theta = %f, beta = %f" % (self.theta_rbk, self.beta_rbk) + mbeta = abs(self.beta1 - self.beta_rbk) > 0.0001 + mtheta = abs(self.theta1 - self.theta_rbk) > 0.0001 + if mbeta: + self.set_beta = self.beta1 + if mtheta: + self.set_theta = self.theta1 + if not mbeta and not mtheta: + self.msg = "start point reached" + else: + self.msg = "timeout while moving to start" + return False + + if mbeta or mtheta: + self.msg = "failed to reach start point" + print self.msg + return False + print self.msg + + maxspeed = 0.03 + tr_time = self.travel_time * 60 + dbeta = abs(self.beta2 - self.beta1) + dtheta = abs(self.theta2 - self.theta1) + mbeta = dbeta > 0.0001 + mtheta = dtheta > 0.0001 + print "dtheta = %f, dbeta = %f\n" % (dtheta, dbeta) + vtheta = dtheta / tr_time + vbeta = dbeta / tr_time + print "vtheta = %f, vbeta = %f\n"%(vtheta,vbeta) + if vtheta > maxspeed or not mtheta: + vtheta = maxspeed + if vbeta > maxspeed or not mbeta: + vbeta = maxspeed + + (self.mm.VBAS, self.gm.VBAS) = (vtheta/10, vbeta/10) + time.sleep (0.1) + (self.mm.VELO, self.gm.VELO) = (vtheta, vbeta) + time.sleep (0.1) + print "mirror: velo=%f vbas=%f acc=%f" % (self.mm.VELO, self.mm.VBAS, self.mm.ACCL) + print "grating: velo=%f vbas=%f acc=%f\n" % (self.gm.VELO, self.gm.VBAS, self.gm.ACCL) + + #self.outparam['pb_len']=int(E2-E1) + self.ResetPvsData() + if mbeta: + self.set_beta = self.beta2 + if mtheta: + self.set_theta = self.theta2 + result = self.waitbusy() + if result: + self.msg = "flying to destination..." + else: + self.msg = "timeout while starting scan" + print self.msg + return result + + def check_endpoint(self): + """ + returns true if the scan has passed the end point, False otherwise. + """ + result = ((self.beta_rbk - self.beta2) * (self.beta2 - self.beta1) > 0) \ + or ((self.theta_rbk - self.theta2) * (self.theta2 - self.theta1) > 0) + return result + + def otfp(self, E1, E2, tr_time = 2): + ''' + starts an energy scan + obsolete - use set_EnergyScan() and start_scan() instead + ''' + self.set_EnergyScan(E1, E2, tr_time) + self.start_scan() + +######################################################################## + def __getattr__(self, attrname): + try: + if (attrname == 'E'): return self.pvs['E'].getw() + elif (attrname == 'E_rbk'): return self.pvs['E_rbk'].getw() + elif (attrname == 'E_crbk'): return self.pvs['E_crbk'].getw() + elif (attrname == 'cff'): return self.pvs['cff'].getw() + elif (attrname == 'cff_rbk'): return self.pvs['cff_rbk'].getw() + elif (attrname == 'theta'): return self.pvs['theta'].getw() + elif (attrname == 'theta_rbk'): return self.pvs['theta_rbk'].getw() + elif (attrname == 'beta'): return self.pvs['beta'].getw() + elif (attrname == 'beta_rbk'): return self.pvs['beta_rbk'].getw() + elif (attrname == 'alldone'): return self.xpvs['alldone'].getw() + elif (attrname == 'dmov'): return self.pvs['dmov'].getw() + elif (attrname == 'grating'): return self.pvs['grating'].getw() + elif (attrname == 'K1'): return self.pvs['K1'].getw() + elif (attrname == 'K2'): return self.pvs['K2'].getw() + #else: raise AttributeError, attrname + else: return self.__dict__[attrname] + except: + msg = 'epicsPGM() __getattr__ %s %s'%(sys.exc_info()[0],sys.exc_info()[1]) + print msg + + def __setattr__(self, attrname, value): + try: + if (attrname == 'E'): self.pvs['E'].putw(value) + elif (attrname == 'set_E'): self.setpvs['set_E'].putw(value) + elif (attrname == 'HALT'): self.xpvs['HALT'].putw(1) + elif (attrname == 'set_cff'): self.pvs['set_cff'].putw(value) + elif (attrname == 'set_theta'): self.setpvs['set_theta'].putw(value) + elif (attrname == 'set_beta'): self.setpvs['set_beta'].putw(value) + #else: raise AttributeError, attrname + else: self.__dict__[attrname] = value + except: + msg = 'epicsPGM() __setattr__ %s %s'%(sys.exc_info()[0],sys.exc_info()[1]) + print msg + + def xmcdgo(self): + raise Exception, "xmcdgo method not implemented for PEARL" + + +#!go############################## + def otfgo(self): + ''' + handles energy scan command + ''' + if self.running: + return + print "otfgo..." + self.pntcnt = 0 + (E1,E2,ttime)=self.uparam['otf'] + self.running = 1 + self.set_EnergyScan(E1, E2, ttime) + self.start_scan() + self.thread = OTFscan_th(self) + + def go_angle(self): + ''' + handles angle scan command + ''' + if self.running: + return + print "go_angle..." + self.pntcnt = 0 + (beta1, beta2, theta1, theta2, ttime) = self.uparam['angles'] + self.running = 1 + self.set_AngleScan(beta1, beta2, theta1, theta2, ttime) + self.start_scan() + self.thread = OTFscan_th(self) + + def stop(self): + try: + self.thread.running = 0 + self.thread.join() + except: + pass + self.HALT=1 + self.setDft() + + def getdata(self): + #if not self.pvs['K1'].checkMonitor(): + # return + + if self.pntcnt == 0: + self.pvsdata['elapsed_time_secs'] = [time.time() - self.start] + self.pvsdata['timestamp'] = [time.ctime()] + for pv in self.pvs.keys(): + self.pvsdata[pv] = [self.pvs[pv].getw()] + else: + self.pvsdata['elapsed_time_secs'].append(time.time() - self.start) + self.pvsdata['timestamp'].append(time.ctime()) + for pv in self.pvs.keys(): + self.pvsdata[pv].append(self.pvs[pv].getw() ) + self.pntcnt +=1 + + #tstamp =time.strftime("%d/%b/%y %H:%M:%S",time.localtime()) + + def isbusy(self): + if self.alldone: + return 0 + else: + return 1 + + def waitbusy(self): + """ + wait until motors have started + """ + tick = 0 + #print 'waitbusy...' + while not self.isbusy(): + time.sleep(0.1) + tick = tick+1 + if tick >= 5: + return 0 + #print '.' + return 1 + + def waitdone(self, timeout = 100): + self.waitbusy() + tick = 0 + while self.isbusy(): + if not self.running: + break + time.sleep(0.1) + tick = tick+1 + self.msg = "current energy %.1f eV" % (self.E_crbk) + if tick >= timeout: + self.msg = "TIMEOUT" + return 0 + return 1 + +def test(): + gds = epicsPGM() + gds.ResetPvsData() + gds.getdata() + print str(gds.pvsdata) + gds.ResetPvsData() + gds.getdata() + print str(gds.pvsdata) + +if __name__ == '__main__': + test()