Support for various app and slic wrapper

This commit is contained in:
2023-06-13 14:18:09 +02:00
parent f22a17852c
commit dc5df0ca2a
13 changed files with 488 additions and 31 deletions

View File

@ -1,2 +1,4 @@
from .adaptiveorbit import AdaptiveOrbit from .adaptiveorbit import AdaptiveOrbit
from .spectralanalysis import SpectralAnalysis from .spectralanalysis import SpectralAnalysis
from .hero import LaserPower,EnergyModulation
from .dispersiontools import Dispersion

View File

@ -39,7 +39,7 @@ class AdaptiveOrbit:
def initBSStream(self,channels): def initBSStream(self,channels):
print("Initializing BSstream") print("Initializing BSstream")
bs = BSCache() bs = BSCache(100000,10000) # 1 second time out, capazity for 100 second.
bs.stop() bs.stop()
for cnl in channels[1:]: for cnl in channels[1:]:
if not is_available(cnl): if not is_available(cnl):
@ -63,10 +63,8 @@ class AdaptiveOrbit:
return pvs return pvs
def flush(self): def flush(self):
with self.bsAR.pt.queue.mutex: self.bsAR.flush()
self.bsAR.pt.queue.queue.clear() self.bsAT.flush()
with self.bsAT.pt.queue.mutex:
self.bsAT.pt.queue.queue.clear()
def terminate(self): def terminate(self):
print('Stopping BSStream Thread...') print('Stopping BSStream Thread...')

175
app/dispersiontools.py Normal file
View File

@ -0,0 +1,175 @@
import datetime
import re
import numpy as np
from bsread import dispatcher
import epics
from slic.core.adjustable import PVAdjustable
from slic.core.acquisition import BSAcquisition
from slic.core.scanner import Scanner
def getAux(pvs=None):
if not pvs:
return
ret={}
val = epics.caget_many(pvs)
for i,pv in enumerate(pvs):
if val[i]: # filter out None values
ret[pv]=float(val[i])
epics.ca.clear_cache()
return ret
def getBSChannels(regexp):
prog = re.compile(regexp)
res = []
for bs in dispatcher.get_current_channels():
if prog.match(bs['name']):
res.append(bs['name'])
return res
class Dispersion:
def __init__(self, branch = 'Aramis'):
self.scanname = 'Dispersion'
self.branch = 'None'
dirpath= datetime.datetime.now().strftime('/sf/data/measurements/%Y/%m/%d/slic_sfbd')
self.scandir='%s/%s' % (dirpath,self.scanname)
self.setBranch()
self.sc = None
self.Nsteps = 2
self.Nsamples = 1
def setBranch(self,branch = 'Aramis'):
if branch == 'Athos Dump':
self.setupAthosDump()
elif branch == 'Aramis':
self.setupAramis()
else:
self.branch = 'None'
def setupAramis(self):
# pre-scan item
self.pre = {}
self.pre['SFB_BEAM_DUMP_AR:ONOFF1']={'Val':0,'InitVal':0}
self.pre['SFB_BEAM_ENERGY_ECOL_AR:ONOFF1']={'Val':0,'InitVal':0}
self.pre['SFB_ORBIT_S30:ONOFF1']={'Val':0,'InitVal':0}
self.pre['SFB_ORBIT_SAR:ONOFF1']={'Val':0,'InitVal':0}
for pv in self.pre.keys():
self.pre[pv]['adj']=PVAdjustable(pv)
# adjustable
self.adjSV = 'S30:SET-E-GAIN-OP'
self.adjRB = 'S30:GET-E-GAIN-OP'
self.adj = PVAdjustable(self.adjSV,pvname_readback = self.adjRB, accuracy = 0.1)
self.amp = 20 # the amplitude of the scan, which can be scaled
# acquisition
sensor1 = getBSChannels('SAR.*DBPM.*:[XY]1$')
sensor2 = getBSChannels('S[23].*-RLLE-DSP:.*-VS$')
self.sensor = sensor1+sensor2
self.acq = [BSAcquisition("machine","sfbd", default_channels=self.sensor)]
# auxiliar data to be read one
self.aux=[]
for sen in sensor2:
if 'PHASE-VS' in sen:
self.aux.append(sen.replace('PHASE-VS','GET-VSUM-PHASE-OFFSET').replace('RLLE-DSP','RSYS'))
self.aux.append(sen.replace('PHASE-VS','GET-VSUM-AMPLT-SCALE').replace('RLLE-DSP','RSYS'))
self.aux.append(sen.replace('PHASE-VS','SM-SET').replace('RLLE-DSP','RMSM'))
self.aux.append('S10BC02-MBND100:ENERGY-OP')
# scanner
self.branch='Aramis'
self.path = '%s-%s' % (self.scanname,self.branch)
self.scanner = Scanner(data_base_dir=self.path,scan_info_dir=self.path,
make_scan_sub_dir=True,
default_acquisitions=self.acq)
def setupAthosDump(self):
# pre-scan item
self.pre = {}
self.pre['SFB_BEAM_DUMP_AT:ONOFF1']={'Val':0,'InitVal':0}
self.pre['SFB_ORBIT_SAT:ONOFF1']={'Val':0,'InitVal':0}
for i in range(1,5):
self.pre['SFB_ORBIT_SAT_%2.2d:ONOFF1' % i ]={'Val':0,'InitVal':0}
for pv in self.pre.keys():
self.pre[pv]['adj']=PVAdjustable(pv)
# adjustable
self.adjSV = 'SATCB01-RSYS:SET-BEAM-PHASE'
self.adjRB = 'SATCB01-RSYS:GET-BEAM-PHASE'
self.adj = PVAdjustable(self.adjSV,pvname_readback = self.adjRB, accuracy = 0.1)
self.amp = 20 # the amplitude of the scan, which can be scaled
# acquisition
self.sensor = ['SATBD02-DBPM010:Y2','SATBD02-DBPM040:Y2']
self.acq = [BSAcquisition("machine","sfbd", default_channels=self.sensor)]
# auxiliar data to be read one
aux = ['SATCL01-MBND100:ENERGY-OP']
# scanner
self.branch='Athos_Dump'
self.path = '%s-%s' % (self.scanname,self.branch)
self.scanner = Scanner(data_base_dir=self.path,scan_info_dir=self.path,
make_scan_sub_dir=True,
default_acquisitions=self.acq)
def setup(self,scl = 1, Nsteps=5, Nsamples=5):
val = self.adj.get_current_value(readback=False)
dval = self.amp*scl
self.N = Nsteps
self.Ns= Nsamples
self.values=np.linspace(val-dval,val+dval,num=self.N)
def preaction(self):
for key in self.pre.keys():
self.pre[key]['InitVal'] = self.pre[key]['adj'].get_current_value(readback = False)
self.pre[key]['adj'].set_target_value(self.pre[key]['Val'])
def postaction(self):
for key in self.pre.keys():
self.pre[key]['adj'].set_target_value(self.pre[key]['InitVal'])
def scan(self):
self.sc=self.scanner.ascan_list(self.adj,self.values,
filename=self.scanname,start_immediately = False,
n_pulses=self.Ns,return_to_initial_values=True)
self.preaction()
self.sc.run()
self.auxdata = getAux(self.aux)
self.postaction()
def stop(self):
if self.sc is None:
return
self.sc.stop()
def running(self):
return self.sc.running
def status(self):
si = self.sc.scan_info.to_dict()
steps = 0
if 'scan_values' in si:
steps=len(si['scan_values'])
return steps,self.N
def info(self):
return self.sc.scan_info.to_dict()

111
app/hero.py Normal file
View File

@ -0,0 +1,111 @@
import datetime
import numpy as np
from slic.core.acquisition import PVAcquisition
from slic.core.acquisition import BSAcquisition
from slic.devices.general import motor
from slic.core.scanner import Scanner
from sfbd.ext import CamAcquisition
# some temporary wrapper
class PollingPVAcquisition(PVAcquisition):
def _acquire(self, *args, polling=True, **kwargs):
return super()._acquire(*args, polling=polling, **kwargs)
class LaserScanBase:
def __init__(self):
print('Init Base Class')
self.SV= 'SSL-LMOT-M1104:MOT'
self.pol = motor.Motor(self.SV)
def stop(self):
if self.sc is None:
return
self.sc.stop()
def running(self):
return self.sc.running
def status(self):
si = self.sc.scan_info.to_dict()
steps = 0
if 'scan_values' in si:
steps=len(si['scan_values'])
return steps,self.N
def info(self):
return self.sc.scan_info.to_dict()
def setup(self,amax=21,Nsteps=5,Nsamples=5):
amin = 0
self.N = Nsteps
self.Ns= Nsamples
self.values=np.linspace(19,21,num=self.N) # needs a change
# measuring the pulse energy as a function of the controling PV. Note that the power should be limited to 300 uJ
# thus limiting the value of the actuaor defining the lase rpulse energy in the EnergyModulaiton class.
class LaserPower(LaserScanBase):
def __init__(self):
super(LaserPower,self).__init__()
self.scanname = 'HEROLaserEnergy'
dirpath= datetime.datetime.now().strftime('/sf/data/measurements/%Y/%m/%d/slic_sfbd')
self.scandir='%s/%s' % (dirpath,self.scanname)
self.RB = 'SSL-LENG-SLNK1:VAL_GET'
self.erg = PollingPVAcquisition("machine","sfbd", default_channels=[self.RB])
self.scanner = Scanner(data_base_dir=self.scandir,scan_info_dir=self.scandir,make_scan_sub_dir=True,
default_acquisitions=[self.erg])
def scan(self):
self.sc=self.scanner.ascan_list(self.pol,self.values,
filename=self.scanname,start_immediately = False,
n_pulses=self.Ns,return_to_initial_values=True)
self.sc.run()
# measuring the coherent emission/space charge blow-up as a function of the hero energy modulation
class EnergyModulation(LaserScanBase):
def __init__(self, acq = 0):
super(EnergyModulation,self).__init__()
self.scanname = 'HEROEnergyModulation'
dirpath= datetime.datetime.now().strftime('/sf/data/measurements/%Y/%m/%d/slic_sfbd')
self.scandir='%s/%s' % (dirpath,self.scanname)
self.acq = acq
if self.acq == 0:
self.RB ='SATFE10-PEPG046-EVR0:CALCI'
self.erg = BSAcquisition("machine","sfbd", default_channels=[self.RB])
elif self.acq == 1:
self.RB ='SATBD02-DBPM040:Y2'
self.erg = BSAcquisition("machine","sfbd", default_channels=[self.RB])
elif self.acq == 2:
self.RB = 'SATBD01-DSCR210'
self.erg = CamAcquisition("machine","sfbd", default_channels=[self.RB])
self.erg.getConnection(self.RB)
else:
self.RB = 'SATBD02-DSCR050'
self.erg = CamAcquisition("machine","sfbd", default_channels=[self.RB])
self.erg.getConnection(self.RB)
self.scanner = Scanner(data_base_dir=self.scandir,scan_info_dir=self.scandir,make_scan_sub_dir=True,
default_acquisitions=[self.erg])
def scan(self):
self.sc=self.scanner.ascan_list(self.pol,self.values,
filename=self.scanname,start_immediately = False,
n_pulses=self.Ns,return_to_initial_values=True)
self.sc.run()

View File

@ -11,7 +11,7 @@ class SpectralAnalysis:
""" """
def __init__(self): def __init__(self):
self.bs = BSCache() self.bs = BSCache(100000,10000) # 100 second timeout, size for 100 second data taken
self.bs.stop() self.bs.stop()
self.channel = '' self.channel = ''
@ -35,8 +35,7 @@ class SpectralAnalysis:
self.bs.pt.running.clear() # for some reason I have to self.bs.pt.running.clear() # for some reason I have to
def flush(self): def flush(self):
with self.bs.pt.queue.mutex: self.bs.flush()
self.bs.pt.queue.queue.clear()
def read(self): def read(self):
data=self.bs.__next__() data=self.bs.__next__()

View File

@ -1 +1,3 @@
from .reichebscombined import ReicheBSCombined from .magnet import Magnet
from .camacquisition import CamAcquisition
from .counteradjustable import CounterAdjustable

41
ext/camacquisition.py Normal file
View File

@ -0,0 +1,41 @@
from time import sleep
from tqdm import trange
import h5py
from cam_server_client import PipelineClient
from cam_server_client.utils import get_host_port_from_stream_address
from bsread import source, SUB
from slic.core.acquisition.acquisition import Acquisition
class CamAcquisition(Acquisition):
def getConnection(self,cam):
pipeline_client = PipelineClient()
cam_instance_name = str(cam) + "_sp1"
stream_address = pipeline_client.get_instance_stream(cam_instance_name)
self.host, self.port = get_host_port_from_stream_address(stream_address)
print(self.host,self.port)
def _acquire(self, filename, channels=None, data_base_dir=None, scan_info=None, n_pulses=100, **kwargs):
print("my routine")
print("extra kwargs:", kwargs)
args = (filename, n_pulses, channels)
args = ", ".join(repr(i) for i in args)
print("acquire({})".format(args))
print(f"dummy acquire to {filename}:")
# stream_host,stream_port = getPipeLine(channels[0])
# time.wait(1)
data= []
with source(host=self.host, port=self.port, mode=SUB) as input_stream:
input_stream.connect()
for i in range(n_pulses):
print('Camera Images', i)
message = input_stream.receive()
data.append(message.data.data)
hid = h5py.File(filename,'w')
gid = hid.create_group(channels[0])
for key in data[0].keys():
gid.create_dataset(key, data = [rec[key].value for rec in data])
hid.close()

23
ext/counteradjustable.py Normal file
View File

@ -0,0 +1,23 @@
from slic.core.adjustable import Adjustable
class CounterAdjustable(Adjustable):
def __init__(self, adjustable1, adjustable2):
self.adj1=adjustable1
self.adj2=adjustable2
self.ref_values() # implementation needs reference values to convert absolute scan to relative scan
def ref_value(self):
self.val1 = self.adj1.get_current_value(readback = False)
self.val2 = self.adj2.get_current_value(readback = False)
def set_target_value(self, value):
t1 = self.adj1.set_target_value(self.val1 + value)
t2 = self.adj2.set_target_value(self.val2 - value)
t1.wait()
t2.wait()
def get_current_value(self):
return self.adj1.get_current_value()
def is_moving(self):
return any([self.adj1.is_moving(),self.adj2.is_moving()])

21
ext/magnet.py Normal file
View File

@ -0,0 +1,21 @@
from slic.core.adjustable import PVAdjustable
from slic.utils import typename
class Magnet(PVAdjustable):
def __init__(self,name):
self.name=name
pvsv='%s:I-SET' % name
pvrb='%s:I-READ' % name
tol = 0.075
super().__init__(pvsv,pvname_readback=pvrb,accuracy=tol,internal=True)
@property
def status(self):
return "Cycling"
def __repr__(self):
tn = typename(self)
return f"{tn} \"{self.name}\" is {self.status}"

View File

@ -1,12 +0,0 @@
from slic.core.sensor.bsmonitor import BSMonitor
class ReicheBSCombined(BSMonitor):
# Du brauchst kein extra init. BSMonitor tut schon das richtige...
def _unpack(self, data):
# data ist ein dict mit allen Deinen Kanälen
pid = data["pid"] # der effektive Channel-Name der Pulse ID
# hier dein Code
# am Ende sollte eine Zahl rauskommen:
return data

View File

@ -2,3 +2,4 @@ from .snap import getSnap
from .save import saveDataset from .save import saveDataset
from .load import loadDataset from .load import loadDataset
from .elog import writeElog from .elog import writeElog
from .slic import SlicScan

View File

@ -50,7 +50,8 @@ def saveDataset(program,data,actuator=None,snap=None,analysis=None,figures=None)
writeSnap(hid,snap) writeSnap(hid,snap)
hid.close() hid.close()
writeFigure(filename,figures) if figures:
writeFigure(filename,figures)
return filename return filename
@ -83,6 +84,16 @@ def openDataset(program):
def writeData(hid, data, scanrun=1): def writeData(hid, data, scanrun=1):
# write the sensor raw value
for ele in data.keys():
name=ele.split(':')
if len(name)>1:
dset=hid.create_dataset('scan_%d/data/%s/%s' % (scanrun, name[0], name[1]), data=data[ele])
else:
dset=hid.create_dataset('scan_%d/data/%s' % (scanrun, name[0]), data=data[ele])
dset.attrs['system'] = getDatasetSystem(name[0])
dset.attrs['units'] = 'unknown'
# this part is obsolete - dimension should be given from the individual datasets
if not 'pid' in data.keys(): if not 'pid' in data.keys():
return return
shape = data['pid'].shape shape = data['pid'].shape
@ -95,15 +106,7 @@ def writeData(hid, data, scanrun=1):
hid.create_dataset("scan_%d/method/samples" % scanrun,data=[nsam]) hid.create_dataset("scan_%d/method/samples" % scanrun,data=[nsam])
hid.create_dataset("scan_%d/method/dimension" % scanrun,data=[ndim]) hid.create_dataset("scan_%d/method/dimension" % scanrun,data=[ndim])
hid.create_dataset("scan_%d/method/reducedData" % scanrun,data=[0]) # indicating that there is at least a 2D array for scalar data hid.create_dataset("scan_%d/method/reducedData" % scanrun,data=[0]) # indicating that there is at least a 2D array for scalar data
# write the sensor raw value
for ele in data.keys():
name=ele.split(':')
if len(name)>1:
dset=hid.create_dataset('scan_%d/data/%s/%s' % (scanrun, name[0], name[1]), data=data[ele])
else:
dset=hid.create_dataset('scan_%d/data/%s' % (scanrun, name[0]), data=data[ele])
dset.attrs['system'] = getDatasetSystem(name[0])
dset.attrs['units'] = 'unknown'
def writeActuator(hid,act,scanrun=1): def writeActuator(hid,act,scanrun=1):
if not act: if not act:

93
interface/slic.py Normal file
View File

@ -0,0 +1,93 @@
import h5py
import numpy as np
import time
from threading import Thread
from PyQt5.QtCore import QObject, pyqtSignal
# to do:
# 1 - check if scan thread is running
# 2 - import of BSread data
from sfbd.interface import getSnap
class SlicScan(QObject):
siginc = pyqtSignal(int, int) # signal for increment
sigterm = pyqtSignal(int) # signal for termination
sigsnap = pyqtSignal(bool)
def __init__(self):
QObject.__init__(self)
self.clear()
def clear(self):
self.daq = None
self.data = None
self.act = None
self.snap = None
def start(self,daq,snap=False):
self.clear()
Thread(target=self.Tmonitor).start()
self.startSnap(snap)
def startSnap(self,snap=False):
if not snap:
Thread(target=self.Tsnap).start()
def Tsnap(self):
self.snap = getSnap()
self.sigsnap.emit(True)
def Tmonitor(self):
mythread = Thread(target=self.Tscanner).start()
time.sleep(1)
ostep = -1
while(self.daq.running()):
istep,nstep=self.daq.status()
if istep>ostep:
ostep=istep
self.siginc.emit(istep,nstep)
time.sleep(1)
if not mythread == None: # wait till scanning thread is done
mythread.join()
istep,nstep=self.daq.status()
self.siginc.emit(istep,nstep)
self.data,self.act = importSlicScan(self.daq.info())
if hasattr(self.daq,'auxdata'):
self.data.update(self.daq.auxdata)
self.sigterm.emit(istep==nstep)
def Tscanner(self):
self.daq.scan()
def stop(self):
self.daq.stop()
def importSlicScan(scan_info):
if not isinstance(scan_info,dict):
return None,None
if not 'scan_files' in scan_info.keys():
return None,None
sfiles = scan_info['scan_files']
data = {}
for istep, sfile in enumerate(sfiles):
hid = h5py.File(sfile[0],'r')
for name, h5obj in hid.items():
if isinstance(h5obj,h5py.Dataset): # pv channels
data[name] = addDatasetToData(data,name,h5obj)
elif isinstance(h5obj,h5py.Group): # bs read channels
if 'data' in h5obj:
data[name] = addDatasetToData(data,name,h5obj['data'])
actuator = {}
name = scan_info['scan_parameters']['name'][0]
actuator[name]=np.array(scan_info['scan_values'])
data[name]=np.array(scan_info['scan_readbacks'])
return data,actuator
def addDatasetToData(data,name,h5obj):
if not name in data:
return np.array([h5obj[()]])
else:
return np.append(data[name],np.array([h5obj[()]]),axis=0)