262 lines
8.7 KiB
Python
262 lines
8.7 KiB
Python
import datetime
|
|
import re
|
|
import numpy as np
|
|
import yaml
|
|
import os
|
|
import json
|
|
|
|
import copy
|
|
|
|
from threading import Thread
|
|
|
|
import epics
|
|
|
|
class Snap:
|
|
def __init__(self,filename='/sf/data/applications/snapshot/req/op/SF_settings.yaml'):
|
|
|
|
self.pvs = self.parseYAML(filename)
|
|
self.doAbort = False
|
|
|
|
def my_caget_many(self):
|
|
# this skips quite some channels - don't know why?
|
|
|
|
pvdata = {}
|
|
pvchids = []
|
|
# create, don't connect or create callbacks
|
|
for name in self.pvs:
|
|
chid = epics.ca.create_channel(name, connect=False, auto_cb=False) # note 1
|
|
pvchids.append(chid)
|
|
|
|
# connect
|
|
for chid in pvchids:
|
|
print(epics.ca.name(chid))
|
|
epics.ca.connect_channel(chid)
|
|
|
|
# request get, but do not wait for result
|
|
epics.ca.poll()
|
|
for chid in pvchids:
|
|
print(epics.ca.name(chid))
|
|
epics.ca.get(chid, wait=False) # note 2
|
|
|
|
# now wait for get() to complete
|
|
epics.ca.poll()
|
|
for chid in pvchids:
|
|
print(epics.ca.name(chid))
|
|
val = epics.ca.get_complete(chid,timeout=0.5)
|
|
if not val:
|
|
pvdata[epics.ca.name(chid)] = np.array([val])
|
|
epics.ca.clear_cache()
|
|
return pvdata
|
|
|
|
def abort(self):
|
|
self.doAbort=True
|
|
print('Aborting Snap')
|
|
|
|
def getSnapValues(self,force=True):
|
|
self.doAbort=False
|
|
ret={}
|
|
if self.pvs:
|
|
# ret=self.my_caget_many()
|
|
val = epics.caget_many(self.pvs)
|
|
for i,pv in enumerate(self.pvs):
|
|
if val[i]: # filter out None values
|
|
ret[pv]={'val':float(val[i])}
|
|
epics.ca.clear_cache()
|
|
return ret,{}
|
|
|
|
#-------------
|
|
# routines to parse the OP YAML file
|
|
|
|
def applyMacro(self,pvs_in,macros):
|
|
pvs = []
|
|
for macro in macros:
|
|
for key in macro:
|
|
tag='$('+key+')'
|
|
for pv in pvs_in:
|
|
if tag in pv:
|
|
pvs.append(pv.replace(tag,macro[key]))
|
|
for pv in pvs_in: # copy the ones without macro
|
|
if not '$(' in pv:
|
|
pvs.append(pv)
|
|
return pvs
|
|
|
|
def parseYAML(self,filename='/sf/data/applications/snapshot/req/op/SF_settings.yaml'):
|
|
pvs = []
|
|
path = os.path.dirname(filename)
|
|
with open(filename) as f:
|
|
try:
|
|
content = yaml.load(f, Loader=yaml.SafeLoader)
|
|
if 'include' in content.keys():
|
|
if len(content['include']) > 0:
|
|
for cont in content['include']:
|
|
retpv = self.parseYAML(path+'/'+cont['name'])
|
|
if 'macros' in cont.keys():
|
|
retpv = self.applyMacro(retpv,cont['macros'])
|
|
pvs = pvs + retpv
|
|
if 'pvs' in content.keys():
|
|
if 'list' in content['pvs']:
|
|
for pv in content['pvs']['list']:
|
|
pvs.append(pv['name'])
|
|
return pvs
|
|
return None
|
|
|
|
except yaml.YAMLError as e:
|
|
print(e)
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
|
|
class Old:
|
|
|
|
def __init__(self):
|
|
self.filename = filename
|
|
print('Estbalishing snapshot with request file:', filename,flush=True)
|
|
self.savepath = savepath
|
|
self.tolerance = 0.0005
|
|
self.pvnames = []
|
|
self.pvs = []
|
|
self.mppvnames = []
|
|
self.mppvs = []
|
|
self.machinepar = []
|
|
self.message = ''
|
|
if self.filename:
|
|
self.openRequestFile(self.filename)
|
|
|
|
def openRequestFile(self, filename):
|
|
self.filename = filename
|
|
self.rootname = self.filename.split('/')[-1]
|
|
|
|
isReq = True
|
|
if '.yaml' in filename:
|
|
isReq = False
|
|
# req_file = SnapshotReqFile(path=str(self.filename))
|
|
|
|
if newVersion:
|
|
if '.yaml' in filename:
|
|
req_file = SnapshotJsonFile(path=str(self.filename))
|
|
else:
|
|
req_file = SnapshotReqFile(path=str(self.filename))
|
|
pvs_list= req_file.read()[0]
|
|
print('PV List:-------------------------')
|
|
for i in range(len(pvs_list)):
|
|
print(pvs_list[i])
|
|
print(req_file.read()[1])
|
|
else:
|
|
if '.yaml' in filename:
|
|
self.filename=None
|
|
self.rootname = None
|
|
print('YAML files not supported')
|
|
return
|
|
req_file = SnapshotReqFile(str(self.filename))
|
|
pvs_list = req_file.read()
|
|
|
|
|
|
self.pvnames.clear()
|
|
self.machinepar.clear()
|
|
for ele in pvs_list:
|
|
if isinstance(ele, list):
|
|
self.pvnames = ele
|
|
elif isinstance(ele, dict):
|
|
if 'machine_params' in ele.keys():
|
|
self.machinepar = ele['machine_params']
|
|
Thread(target=self.connectPVs).start()
|
|
|
|
def connectPVs(self):
|
|
self.pvs = [PV(pv, auto_monitor=False) for pv in self.pvnames]
|
|
con = [pv.wait_for_connection(timeout=0.2) for pv in self.pvs]
|
|
pvscon=[]
|
|
for i, val in enumerate(con):
|
|
if val is False:
|
|
print('Cannot connect to PV:', self.pvs[i].pvname,flush=True)
|
|
else:
|
|
pvscon.append(self.pvs[i])
|
|
self.pvs = copy.deepcopy(pvscon)
|
|
if isinstance(self.machinepar,list):
|
|
self.mppvs = [PV(self.machinepar[key], auto_monitor=False) for key in self.machinepar]
|
|
else:
|
|
self.mppvs = [PV(self.machinepar[key], auto_monitor=False) for key in self.machinepar.keys()]
|
|
con = [pv.wait_for_connection(timeout=0.2) for pv in self.mppvs]
|
|
pvscon.clear()
|
|
for i, val in enumerate(con):
|
|
if val is False:
|
|
print('Cannot connect to mPV:', self.mppvs[i].pvname,flush=True)
|
|
else:
|
|
pvscon.append(self.mppvs[i])
|
|
self.mppvs=copy.deepcopy(pvscon)
|
|
|
|
def getSnapValues(self, force=True):
|
|
values = {}
|
|
val = [pv.get(timeout=0.6, use_monitor=False) for pv in self.pvs]
|
|
for i, pv in enumerate(self.pvs):
|
|
if val[i] is None:
|
|
if force:
|
|
continue
|
|
else:
|
|
return False
|
|
else:
|
|
values[pv.pvname] = {
|
|
"raw_name": pv.pvname, "val": val[i], "EGU": pv.units, "prec": pv.precision}
|
|
mvalues = {}
|
|
val = [pv.get(timeout=0.6, use_monitor=False) for pv in self.mppvs]
|
|
for i, pv in enumerate(self.mppvs):
|
|
if val[i] is None:
|
|
if force:
|
|
continue
|
|
else:
|
|
return False
|
|
else:
|
|
mvalues[pv.pvname] = {"value": val[i],
|
|
"units": pv.units, "precision": pv.precision}
|
|
return values, mvalues
|
|
|
|
def save(self, labels=[], comment="Generated by SFBD-Package", force=True):
|
|
if self.filename is None:
|
|
self.message = 'No Request File Loaded'
|
|
return False
|
|
|
|
val, mval = self.getSnapValues(force)
|
|
if isinstance(val, bool) and val == False:
|
|
self.message = 'Unsuccesful reading of PV channels (unforced access)'
|
|
return False
|
|
|
|
# construct file name
|
|
datetag = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
root = self.rootname.split('.req')[0]
|
|
files = self.savepath+root+'_'+datetag+'.snap'
|
|
filel = self.savepath+root+'_latest.snap'
|
|
|
|
# reshuffle from mval to keyword based machine values
|
|
mmval = {}
|
|
for key in self.machinepar.keys():
|
|
if self.machinepar[key] in mval.keys():
|
|
mmval[key] = mval[self.machinepar[key]]
|
|
# save file
|
|
parse_to_save_file(
|
|
val, files, macros=None, symlink_path=filel, comment=comment,
|
|
labels=[],
|
|
req_file_name=self.rootname, machine_params=mmval)
|
|
self.message = 'Snapshot saved to '+files
|
|
return True
|
|
|
|
def restore(self,filename,refilter='',force=True):
|
|
filepath=self.savepath+filename
|
|
prog=re.compile(refilter)
|
|
save_pvs=parse_from_save_file(filepath)
|
|
res={}
|
|
for ele in save_pvs:
|
|
if isinstance(ele,dict):
|
|
for key in ele.keys():
|
|
if prog.match(key):
|
|
res[key]=ele[key]['value']
|
|
|
|
for pv in self.pvs:
|
|
if pv.pvname in res.keys():
|
|
val=pv.get()
|
|
if val is None or np.abs(val-res[pv.pvname]) > self.tolerance:
|
|
pv.put(res[pv.pvname])
|
|
self.message ='Snap restored'
|
|
|
|
|