diff --git a/ext/bscacquisition.py b/ext/bscacquisition.py index 837ac55..b2b9c39 100644 --- a/ext/bscacquisition.py +++ b/ext/bscacquisition.py @@ -11,6 +11,7 @@ class BSCAcquisition(Acquisition): def _acquire(self, filename, channels=None, data_base_dir=None, scan_info=None, n_pulses=100, **kwargs): + queue =channels[0] # abusing interface since BSAcquisition assume a list of channels # allocating space @@ -27,7 +28,6 @@ class BSCAcquisition(Acquisition): for chn in chns: data[chn]['data'][i] = msg[chn] data['pulse_id'][i]=msg['pid'] - # write out the data file hid = h5py.File(filename,'w') hid.create_dataset('pulse_id', data = data['pulse_id']) diff --git a/interface/__init__.py b/interface/__init__.py index 7aca08e..ba53623 100644 --- a/interface/__init__.py +++ b/interface/__init__.py @@ -12,3 +12,4 @@ from .slic import SlicScan from .slic import importSlicScan from .doocs import doocsread from .doocs import doocswrite + diff --git a/interface/doocs.py b/interface/doocs.py new file mode 100644 index 0000000..11c52f2 --- /dev/null +++ b/interface/doocs.py @@ -0,0 +1,33 @@ +import subprocess + + +def doocsread(mystr): + """ mystr input can be a string or a list """ + if isinstance(mystr, list): # it's a list + readCmd = ' '.join(mystr) + else: + readCmd = mystr + cmd = ['doocsget', '-c', readCmd] + MyOut = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + stdout, stderr = MyOut.communicate() + if b'result->error()' in stdout.split(): + print('ERROR: reading error') + print(mystr) + return + result = [element.decode('utf-8') for element in stdout.split()] + if len(result) == 1: # one element only + try: + return float(result[0]) + except Exception as e: + return result[0] + else: + try: + return [float(x) for x in result] + except Exception as e: + return result + + +def doocswrite(mystr, target): + cmd = ['doocsput', '-c', mystr, '-d', str(target)] + MyOut = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + stdout, stderr = MyOut.communicate() diff --git a/interface/load.py b/interface/load.py index 9d82b6d..ce28a10 100644 --- a/interface/load.py +++ b/interface/load.py @@ -5,21 +5,24 @@ def loadDataset(filename): hid = h5py.File(filename, "r") icount = 0 snap = loadSnap(hid) - data =[] - act = [] + data={} + act= {} + maxID = 0 for key in hid.keys(): if 'scan' in key: ID = int(key.split('_')[1]) + if ID > maxID: + maxID = ID print('Reading scan_%d' % ID) - data.append(loadData(hid,ID)) - act.append(loadActuator(hid,ID)) + data[ID]=loadData(hid,ID) + act[ID]=loadActuator(hid,ID) icount +=1 hid.close() if icount == 0: return None, None, None elif icount == 1: return data[0], act[0],snap - return data,act,snap + return [data[j] for j in range(maxID+1)],[act[j] for j in range(maxID+1)],snap def loadSnap(hid): snap={} diff --git a/interface/magnet.py b/interface/magnet.py new file mode 100644 index 0000000..8ab567a --- /dev/null +++ b/interface/magnet.py @@ -0,0 +1,153 @@ +from time import sleep +from epics import PV,caget_many, caput_many +import numpy as np +from threading import Thread +from time import sleep + +#from onlinemodel.interface import SwissFELMagnet + +class Magnet: + + def __init__(self, beamline = 'ARAMIS',debug=False, signal = None, cal = None): + self.beamline = beamline + self.debug=debug + self.signal = signal + self.progress = None +# self.cal = SwissFELMagnet() + # getting the array + temp = np.sort(np.array(list(range(0, 11)) + [0.5, 1.5]))[::-1] + temp2 = -1. * temp + cyclingCurrents = np.ones(len(temp) * 2) * 0. + cyclingCurrents[::2] = temp + cyclingCurrents[1::2] = temp2 + self.currents = cyclingCurrents[:-1] + self.cyclingPause = 3 + self.pvcor = [] + self.abort = False + + def doAbort(self): + self.abort = True + + def setMagnetI(self,maglist,vals): + if self.debug: + for i,ele in enumerate(maglist): + print('DEBUG: Setting %s to: %f A' % (ele,vals[i])) + return + caput_many(maglist,vals) + + def setMagnets(self,maglist,magbd,erg): + for mag in maglist.keys(): + Itar = self.cal.QuadrupoleKL2I(magbd[mag],maglist[mag],erg*1e6) + pv = PV('%s:I-SET' % mag) + if not Itar is None: + if self.debug: + self.signal.emit('DEBUG: Setting %s to %f' % (pv.pvname,Itar)) + else: + pv.value = Itar + + def scaleMagnets(self,maglist,magbg,EProfCur,EProfTar): + # get current from magnets + for i,mag in enumerate(maglist): + if 'SAR' in mag: + ecur = EProfCur[-1] + etar = EProfTar[-1] + elif 'S30' in mag: + idx = int(mag[5:7]) + if idx >= len(EProfCur): + ecur = EProfCur[-1] + etar = EProfTar[-1] + else: + ecur = EProfCur[idx] + etar = EProfTar[idx] + if 'MQUA' in mag: + pv = PV('%s:I-SET' % mag) + valmag = pv.value + strength = self.cal.QuadrupoleI2KL(magbg[i],valmag,ecur*1e6) + Itar = self.cal.QuadrupoleKL2I(magbg[i],strength,etar*1e6) + elif 'MSEX' in mag: + pv = PV('%s:I-SET' % mag) + valmag = pv.value + strength = self.cal.SextupoleI2KL(magbg[i],valmag,ecur*1e6) + Itar = self.cal.SextupoleKL2I(magbg[i],strength,etar*1e6) + elif 'MBND' in mag: + pv = PV('%s:P-SET' % mag) + valmag=pv.value + strength = pv.value + Itar = valmag-EProfCur[-1]+etar + else: + strength = 0 + if not Itar is None: + if self.debug: + self.signal.emit('DEBUG: Setting %s to %f' % (pv.pvname,Itar)) + else: + pv.value = Itar + + def cycleCorrector(self,QuadList=[],progress=None,done = None): + self.progress = progress + self.done = done # signal to indicate end of thread + self.pvcor = [PV(ele.replace('MQUA','MCRX')) for ele in QuadList]+[PV(ele.replace('MQUA','MCRY')) for ele in QuadList] + self.abort = False + Thread(name='CycleCor',target=self.TcycleCorrector).start() # starting thread which starts and monitors actual thread + + def TcycleCorrector(self): + self.running = True + istep = 0 + nstep = len(self.currents) + while self.running: + if self.progress: + self.progress.emit(istep,nstep) + if self.signal: + self.signal.emit('Setting corrector current to %5.1f A' % self.currents[istep]) + if self.debug: + print('DEBUG: Setting Corrector Magnets to:',self.currents[istep]) + else: + for pv in self.pvcor: + pv.value = cyclingCurrents[istep] + sleep(self.cyclingPause) + istep+=1 + if istep == nstep or self.abort: + self.running = False + if self.progress: + self.progress.emit(nstep,nstep) + if self.done: + self.done.emit(not self.abort) + + + + def cycleMagnets(self,maglist, progress = None, done = None): + if len(maglist) == 0: + done.emit(True) + return -1 + self.progress = progress + self.done = done # signal to indicate end of thread + state = caget_many(['%s:CYCLE-STATE' % mag for mag in maglist]) + magcyc = [mag for i,mag in enumerate(maglist) if not state[i] == 2] + time = caget_many(['%s:CYCLE-PROGFULL' % mag for mag in magcyc]) + if len(time) == 0: + done.emit(True) + return -1 # no cycling needed + imax = np.argmax(time) + self.tcyc = time[imax] + print('Cycling Time:',self.tcyc,'sec for magnet',magcyc[imax]) + if self.debug: + print('DEBUG: Cycle-SET magnets') + else: + caput_many(['%s:CYCLE' % mag for mag in magcyc],[2]*len(magcyc)) + self.abort = False + Thread(name='CycleMag',target=self.TcycleMagnets).start() + return self.tcyc + + def TcycleMagnets(self): + self.running = True + tcur = 0 + while self.running: + sleep(1) + tcur += 1 + if self.progress: + self.progress.emit(tcur,self.tcyc) + if tcur >= self.tcyc or self.abort: + self.running = False + if self.progress: + self.progress.emit(self.tcyc,self.tcyc) + if self.done: + self.done.emit(not self.abort) diff --git a/interface/reprate.py b/interface/reprate.py new file mode 100644 index 0000000..4f7d826 --- /dev/null +++ b/interface/reprate.py @@ -0,0 +1,112 @@ +from time import sleep +from epics import PV +import numpy as np + +class RepRate: + def __init__(self, beamline = 'ARAMIS',debug=False): + self.beamline = beamline + self.debug=debug + self.pv={} + self.pv['BeamDelayAR'] = PV('SIN-TIMAST-TMA:Bunch-1-OnDelay-Sel') + self.pv['BeamDelayAT'] = PV('SIN-TIMAST-TMA:Bunch-2-OnDelay-Sel') + self.pv['RFDelay'] = PV('SIN-TIMAST-TMA:Beam-RF-OnDelay-Sel') + self.pv['StopperAR'] = PV('SARMA02-MBNP100:REQUEST') + self.pv['StopperAT'] = PV('SATMA01-MBNP100:REQUEST') + self.pv['StopperAROut'] = PV('SARMA02-MBNP100:PLC_MOV_OUT') + self.pv['StopperATOut'] = PV('SATMA01-MBNP100:PLC_NOV_OUT') + self.pv['BeamRRAR'] = PV('SIN-TIMAST-TMA:Bunch-1-Freq-Sel') + self.pv['BeamRRAT'] = PV('SIN-TIMAST-TMA:Bunch-2-Freq-Sel') + self.pv['applyTiming'] = PV('SIN-TIMAST-TMA:Beam-Apply-Cmd.PROC') + self.rrReal = np.array([100., 50., 33.3, 25., 16.6, 12.5, 10., 8.3, 5., 2.5, 1.]) + self.rrId = np.arange(0, 11) + + + def setStopper(self,doinsert): + if self.beamline == 'ARAMIS': + pv = self.pv['StopperAR'] + else: + pv = self.pv['StopperAT'] + if self.debug: + print('DEBUG: Setting beam stopper to:',doinsert) + return + if doinsert: + pv.value = 1 + else: + pv.value = 0 + + def isStopperOpen(self): + if self.beamline == 'ARAMIS': + return self.pv['StopperAROut'] + else: + return self.pv['StopperATOut'] + + + def stopSF(self): + if self.debug: + print('DEBUG: Stopping Machine disabled') + return + if self.beamline == 'ARAMIS': + self.pv['BeamDelayAR'].value = 1 + self.pv['BeamDelayAT'].value = 1 + sleep(0.1) + self.pv['RFDelay'].value =1 + sleep(0.1) + self.pv['applyTiming'].value = 1 + + def getRR(self): + if self.beamline == 'ARAMIS': + return self.rrReal[self.pv['BeamRRAR'].value] + else: + return self.rrReal[self.pv['BeamRRAT'].value] + + + def setRR(self,rr=1.): + """ + Possible RR as of 14/01/2020 + 0 100.00 Hz + 1 50.00 Hz + 2 33.33 Hz + 3 25.00 Hz + 4 16.66 Hz + 5 12.50 Hz + 6 10.00 Hz + 7 8.33 Hz + 8 5.00 Hz + 9 2.50 Hz + 10 1.00 Hz + 11 Bunch Off + """ + + findRR = abs(self.rrReal - rr) < 0.1 + if findRR.sum(): # at least one rr is matching + rrSel = self.rrId[np.argmax(findRR)] + else: + print('Requested rep. rate %.3f is not available' % rr) + return + + if self.debug: + print('DEBUG: Setting beam rate to %d (%f Hz)' % (rrSel,rr)) + return + + + if self.beamline == 'ARAMIS': + self.pv['BeamRRAR'].value = rrSel + sleep(0.1) + self.pv['BeamDelayAR'].value = 0 # 0 on beam, 1 on delay + rrAT = self.pv['BeamRRAT'].value + if rrAT < rrSel: # reduce athos at least to same rep rate as Aramis + self.pv['BeamRRAT'].value = rrSel + sleep(0.1) + self.pv['BeamDelayAT'].value = 0 # 0 on beam, 1 on delay + else: + self.pv['BeamRRAT'].value = rrSel + sleep(0.1) + self.pv['BeamDelayAT'].value = 0 # 0 on beam, 1 on delay + + sleep(0.1) + if rrSel == 11: + self.pv['RFDelay'].value =1 + else: + self.pv['RFDelay'].value = 0 + sleep(0.1) + self.pv['applyTiming'].value = 1 diff --git a/interface/rf.py b/interface/rf.py new file mode 100644 index 0000000..148bbbb --- /dev/null +++ b/interface/rf.py @@ -0,0 +1,60 @@ +from epics import PV,caput_many +import numpy as np + +class RF: + def __init__(self, beamline = 'ARAMIS',debug=False, signal = None): + self.beamline = beamline + self.debug=debug + self.signal = signal + if self.beamline == 'ARAMIS': + self.stations = ['S30CB%2.2d' % d for d in range(1,14)] + self.RFamp = [PV('%s-RSYS:SET-ACC-VOLT' % station) for station in self.stations] + self.RFphs = [PV('%s-RSYS:SET-BEAM-PHASE' % station) for station in self.stations] + self.RFon = [PV('%s-RSYS:REQUIRED-OP' % station) for station in self.stations] + self.ErgRange = [PV('SATSY01-MBND200:P-SET'),PV('SARCL02-MBND100:P-SET')] + self.EGain = PV('S30:SET-E-GAIN-OP') + self.EPhase= PV('S30:SET-BEAM-PHASE-OP') + self.Ns = 13 + else: + self.EGain = ['S20:SET-E-GAIN-OP'] + self.Ns = 0 + + def energyProfile(self): + if self.Ns == 0: + return np.array([0,0]) + valAmp = [pv.value for pv in self.RFamp] + valPhs = [pv.value for pv in self.RFphs] + valOn = [pv.value for pv in self.RFon] + valErg = [pv.value for pv in self.ErgRange] + + prof = np.zeros((self.Ns+1)) + prof[0] = valErg[0] + for i in range(self.Ns): + dE = 0 + if valOn[i] == 1: + dE = valAmp[i]*np.sin(np.pi*valPhs[i]/180.) + prof[i+1] = prof[i]+dE + prof = np.array(prof) + Egain = prof - prof[0] + scl = (valErg[1]-valErg[0])/np.max(Egain) + prof = Egain*scl+valErg[0] + return prof + + def changeEnergyGain(self,dE): + if self.debug: + print('DEBUG: Changing energy gain of linac by:',dE) + return + self.EGain.value = dE + if dE > 0: + self.EPhase.value = 90. + else: + self.EPhase.value = -90. + + def setRF(self,chns,vals): + if self.debug: + for i,ele in enumerate(chns): + print('DEBUG: RF-Settings %s: %f' % (ele,vals[i])) + return + caput_many(chns,vals) + + diff --git a/interface/save.py b/interface/save.py index 934c036..c902a84 100644 --- a/interface/save.py +++ b/interface/save.py @@ -19,7 +19,7 @@ def getDatasetFileName(program='Unknown'): path = '%s/%s' % (path,day) if not os.path.exists(path): os.makedirs(path) - datetag = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S_%f') + datetag = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') filename=('%s/%s_%s' % (path, program.replace(' ','_'), datetag)) return filename diff --git a/interface/snap.py b/interface/snap.py index d4a6a3e..8e3c742 100644 --- a/interface/snap.py +++ b/interface/snap.py @@ -73,10 +73,12 @@ def getSnapVal(pvs=None): if not pvs: return None ret={} - val = [pv.value for pv in pvs] + val = [pv.get(timeout=0.1) for pv in pvs] for i,pv in enumerate(pvs): if not val[i] is None: # filter out None values ret[pv.pvname]=float(val[i]) + else: + print('Timeout:',pv.pvname) return ret def saveSnap(pvs={},label="", comment = "generated by application",reqfile = "SF_settings.yaml"): diff --git a/util/simplecapture.py b/util/simplecapture.py new file mode 100644 index 0000000..3eef006 --- /dev/null +++ b/util/simplecapture.py @@ -0,0 +1,38 @@ +import numpy as np + +from bstrd import BSCache + +class SimpleCapture: + def __init__(self): + self.chn=None + self.bs = BSCache(100000,100000) # 1000 second time out, capazity for 1000 second. + self.abort=False + + def terminate(self): + self.bs.stop() + self.bs.pt.running.clear() + + def stop(self): + self.abort=True + + def initChannels(self,channels): + self.chn=channels + self.bs.get_vars(channels) + + def acquire(self,N=1,reprate=-1,stop=False): + self.abort=False + data = np.zeros((N,len(self.chn))) + pid = np.zeros((N)) + self.bs.flush() + idx = 0 + while idx