505 lines
17 KiB
Python
505 lines
17 KiB
Python
###################################################################################################
|
|
# Deployment specific global definitions - executed after startup.py
|
|
###################################################################################################
|
|
|
|
###################################################################################################
|
|
#Devices for PShell standard scans
|
|
###################################################################################################
|
|
|
|
import random
|
|
|
|
####################################################################################################
|
|
# Simulated devices
|
|
####################################################################################################
|
|
|
|
add_device(DummyMotor("m1"), True)
|
|
add_device(DummyMotor("m2"), True)
|
|
add_device(DummyRegister("reg1",3), True)
|
|
add_device(DummyPositioner("p1"),True)
|
|
add_device(MotorGroupBase("mg1", m1, m2), True)
|
|
add_device(MotorGroupDiscretePositioner("dp1", mg1), True)
|
|
|
|
|
|
#Initial Configuration
|
|
if p1.getConfig().unit is None:
|
|
p1.getConfig().minValue = 0.0 #Not persisted
|
|
p1.getConfig().maxValue = 1000.0
|
|
p1.getConfig().unit = "mm"
|
|
p1.getConfig().save()
|
|
p1.initialize()
|
|
|
|
if dp1.getConfig().positions is None:
|
|
dp1.getConfig().positions = ["Park","Ready","Out","Clear"]
|
|
dp1.getConfig().motor1 = ["0.0","4.0","8.0" ,"0.0"]
|
|
dp1.getConfig().motor2 = ["0.0","5.0","3.0" ,"NaN"]
|
|
dp1.getConfig().save()
|
|
dp1.initialize()
|
|
|
|
|
|
|
|
#Update
|
|
m1.setMonitored(True)
|
|
m2.setMonitored(True)
|
|
|
|
####################################################################################################
|
|
# Readable / Writable objects can be created and used in scans
|
|
####################################################################################################
|
|
|
|
class MyWritable(Writable):
|
|
def write(self, value):
|
|
#print ("Write: ",value)
|
|
pass
|
|
|
|
class MyReadable(Readable):
|
|
def read(self):
|
|
return random.random()
|
|
|
|
class MyReadableArray(ReadableArray):
|
|
def read(self):
|
|
ret = []
|
|
for i in range (self.getSize()):
|
|
ret.append(random.random())
|
|
return to_array(ret,'d')
|
|
|
|
def getSize(self):
|
|
return 20
|
|
|
|
class MyReadableArrayNumpy(ReadableArray):
|
|
def read(self):
|
|
ret = numpy.ones(self.getSize(),'d')
|
|
return ret
|
|
|
|
def getSize(self):
|
|
return 20
|
|
|
|
class MyReadableMatrix(ReadableMatrix):
|
|
def read(self):
|
|
ret = []
|
|
for i in range (self.getHeight()):
|
|
ret.append([random.random()] * self.getWidth())
|
|
return to_array(ret, 'd')
|
|
|
|
def getWidth(self):
|
|
return 80
|
|
|
|
def getHeight(self):
|
|
return 40
|
|
|
|
class MyReadableMatrixNumpy(ReadableMatrix):
|
|
def read(self):
|
|
ret = numpy.ones((self.getHeight(), self.getWidth()),'d')
|
|
for i in range(self.getHeight()):
|
|
ret[i]=i
|
|
return to_array(ret, 'd')
|
|
|
|
def getWidth(self):
|
|
return 80
|
|
|
|
def getHeight(self):
|
|
return 40
|
|
|
|
|
|
ao1 = MyWritable("ao1")
|
|
ao2 = MyWritable("ao2")
|
|
ai1 = MyReadable("ai1")
|
|
ai2 = MyReadable("ai2")
|
|
wf1 = MyReadableArray("wf1")
|
|
wf2 = MyReadableArrayNumpy("wf2")
|
|
im1 = MyReadableMatrix("im1")
|
|
im2 = MyReadableMatrixNumpy("im2")
|
|
|
|
|
|
####################################################################################################
|
|
# Imaging
|
|
####################################################################################################
|
|
|
|
configured = os.path.exists(GenericDevice.getConfigFileName("src1"))
|
|
|
|
add_device(RegisterMatrixSource("src1", im1.proxy), True)
|
|
add_device(RegisterMatrixSource("src2", im2.proxy), True)
|
|
|
|
#src1.setPolling(100)
|
|
#src2.setPolling(100)
|
|
|
|
#Some configuration for so the imaging will work out of the box
|
|
if not configured:
|
|
src1.getConfig().colormapAutomatic = True
|
|
src1.getConfig().colormap = Colormap.Temperature
|
|
src1.getConfig().save()
|
|
src2.getConfig().colormapAutomatic = True
|
|
src2.getConfig().save()
|
|
|
|
|
|
|
|
|
|
###################################################################################################
|
|
#Embedding Bluesky
|
|
###################################################################################################
|
|
|
|
|
|
import signal
|
|
signal.signal = lambda *args: None
|
|
from bluesky import RunEngine
|
|
from bluesky.callbacks import CallbackBase
|
|
from bluesky.callbacks.best_effort import BestEffortCallback
|
|
from bluesky.utils import install_kicker, DuringTask
|
|
#from bluesky.utils import ProgressBarManager
|
|
from ophyd.sim import det1, det2, det3, det4, det, motor, motor1, motor2, motor3,img, sig, direct_img, pseudo1x3
|
|
from ophyd import Signal
|
|
from ophyd.signal import EpicsSignal
|
|
from bluesky.plans import count, scan, rel_scan, list_scan, grid_scan, list_grid_scan
|
|
from bluesky.simulators import summarize_plan
|
|
import bluesky.plan_stubs as bps
|
|
from bluesky.plan_stubs import mv
|
|
import bluesky.preprocessors as bpp
|
|
from bluesky.preprocessors import SupplementalData
|
|
#import databroker
|
|
from databroker import Broker
|
|
#import suitcase
|
|
#import suitcase.csv
|
|
|
|
CAN_PAUSE=True
|
|
|
|
|
|
|
|
#def on_ctrl_cmd(cmd):
|
|
def on_abort(parent_thread):
|
|
global RE
|
|
# print ("Control command: ", cmd)
|
|
# if cmd=="abort":
|
|
if "RE" in globals():
|
|
if RE.state not in ['idle','paused', 'pausing']:
|
|
if CAN_PAUSE:
|
|
print ("Abort command: Run Engine aborted")
|
|
sys.stderr=None
|
|
RE.abort("User abort")
|
|
return
|
|
else:
|
|
print ("Abort command: Run Engine pause request")
|
|
RE.request_pause()
|
|
return
|
|
_default_abort(parent_thread)
|
|
|
|
def on_close(parent_thread):
|
|
on_abort(parent_thread)
|
|
|
|
|
|
|
|
|
|
from collections import deque
|
|
|
|
is_scan_paused = False
|
|
class MyHandler(CallbackBase):
|
|
|
|
def __init__(self):
|
|
self.queue= deque(maxlen=1000)
|
|
|
|
def clear(self):
|
|
self.queue.append(("clear",None))
|
|
|
|
def start(self, doc):
|
|
self.queue.append(("start",doc))
|
|
|
|
def stop(self, doc):
|
|
self.queue.append(("stop",doc))
|
|
|
|
def descriptor(self, doc):
|
|
self.queue.append(("descriptor",doc))
|
|
|
|
def resource(self, doc):
|
|
self.queue.append(("resource",doc))
|
|
|
|
def event(self, doc):
|
|
self.queue.append(("event",doc))
|
|
while is_scan_paused:
|
|
time.sleep(0.1)
|
|
|
|
def datum(self, doc):
|
|
self.queue.append(("datum",doc))
|
|
|
|
def event_page(self, doc):
|
|
self.queue.append(("event_page",doc))
|
|
|
|
def datum_page(self, doc):
|
|
self.queue.append(("datum_page",doc))
|
|
|
|
handler= MyHandler()
|
|
re_scan=re_res=None
|
|
|
|
|
|
class EventProcessingTask(DuringTask):
|
|
def __init__(self):
|
|
pass
|
|
|
|
def block(self, blocking_event):
|
|
global start_doc,descriptor_coc, stop_doc, event_doc, re_scan, re_res, __return__,__exception__
|
|
re_scan=None
|
|
while True:
|
|
done = blocking_event.wait(.1)
|
|
if done:
|
|
return
|
|
try:
|
|
check_pause()
|
|
while len(handler.queue):
|
|
(msg, doc) = handler.queue.popleft()
|
|
#print("-> " ,msg)
|
|
if msg=="start":
|
|
global start_doc
|
|
start_doc=doc
|
|
writables=doc.get('motors',[])
|
|
|
|
readables=doc.get('detectors',[])
|
|
start,stop, steps=-1,-1,doc.get('num_intervals',-1)
|
|
try:
|
|
if doc.get("plan_name","")=="grid_scan":
|
|
steps=[x-1 for x in doc['shape']]
|
|
start=[x[0] for x in doc['extents']]
|
|
stop=[x[1] for x in doc['extents']]
|
|
elif doc.get("plan_name","")=="list_grid_scan":
|
|
steps=[-1 for x in doc['shape']]
|
|
start=[x[0] for x in doc['extents']]
|
|
stop=[x[1] for x in doc['extents']]
|
|
elif doc.get("plan_name","")=="scan":
|
|
_,start,stop = doc['plan_args']['args']
|
|
elif doc.get("plan_name","")=="list_scan":
|
|
_,positions=doc['plan_args']['args']
|
|
start = min(positions)
|
|
stop = max(positions)
|
|
except:
|
|
pass
|
|
|
|
diags=None
|
|
monitors=None
|
|
meta={}
|
|
for k in start_doc.keys():
|
|
o=start_doc[k]
|
|
if o is not None:
|
|
if k=="extents":
|
|
o = str(o)
|
|
elif type(o) in (list, tuple):
|
|
o=to_array(o,'s')
|
|
elif type(o) == dict:
|
|
o=str(o)
|
|
meta[k]=o
|
|
re_res = None
|
|
re_scan = ManualScan(writables, readables ,start, stop, steps, diags=diags, monitors=monitors, meta=meta)
|
|
re_scan.scan.setCanPause(CAN_PAUSE)
|
|
re_scan.start()
|
|
|
|
elif msg=="stop":
|
|
global stop_doc
|
|
stop_doc=doc
|
|
if re_scan is not None:
|
|
if not re_scan.scan.isCompleted():
|
|
re_scan.end()
|
|
re_res = re_scan.scan.getResult()
|
|
sys.stderr=jep_stderr
|
|
elif msg=="descriptor":
|
|
global descriptor_doc
|
|
descriptor_doc=doc
|
|
elif msg=="event":
|
|
global event_doc
|
|
event_doc=doc
|
|
if (re_scan is not None) and not (re_scan.scan.isCompleted()):
|
|
setpoints=[]
|
|
readbacks=[]
|
|
detectors=[]
|
|
data=doc['data']
|
|
for dev in start_doc.get("motors",[]):
|
|
readbacks.append(data[dev])
|
|
try:
|
|
setpoints.append(data[dev+"_setpoint"])
|
|
except:
|
|
setpoints.append(data[dev])
|
|
for dev in start_doc.get("detectors",[]):
|
|
v=data[dev]
|
|
if str(type(v))=="<class 'jep.PyJArray'>":
|
|
v=numpy.array(v)
|
|
detectors.append(v)
|
|
re_scan.append (setpoints, readbacks, detectors)
|
|
elif msg=="resource":
|
|
pass
|
|
elif msg=="datum":
|
|
pass
|
|
elif msg=="event_page":
|
|
pass
|
|
elif msg=="datum_page":
|
|
pass
|
|
elif msg=="clear":
|
|
pass
|
|
elif msg=="check_pause":
|
|
pass
|
|
elif msg=="read":
|
|
try:
|
|
(dev)=doc
|
|
__return__ = dev.read()
|
|
except Exception as e:
|
|
__exception__ = e
|
|
elif msg=="write":
|
|
try:
|
|
(dev, value)=doc
|
|
dev.write(value)
|
|
__return__ = True
|
|
except Exception as e:
|
|
__exception__ = e
|
|
except Exception as e:
|
|
#print (e)
|
|
pass
|
|
|
|
|
|
def check_pause():
|
|
try:
|
|
global RE, re_scan, is_scan_paused
|
|
if CAN_PAUSE:
|
|
if ("RE" in globals()) and (re_scan is not None) and not (re_scan.scan.isCompleted()):
|
|
is_scan_paused =re_scan.scan.isPaused() and not re_scan.scan.isAborted()
|
|
else:
|
|
is_scan_paused = False
|
|
#if re_scan.scan.isAborted():
|
|
# if RE.state not in ['idle','paused', 'pausing']:
|
|
# print ("Scan abort")
|
|
# RE.abort("Scan abort")
|
|
"""
|
|
if re_scan.scan.isPaused():
|
|
if RE.state not in ['idle','paused', 'pausing']:
|
|
print ("Scan paused: Run Engine pause request")
|
|
RE.request_pause()
|
|
is_scan_paused = True
|
|
else:
|
|
if RE.state in ['paused', 'pausing']:
|
|
print ("Scan resumed: Run Engine resuming")
|
|
#RE.resume()
|
|
"""
|
|
except:
|
|
pass
|
|
|
|
|
|
class ReaderWrapper():
|
|
def __init__(self, dev):
|
|
self.dev=dev
|
|
self.name=dev.getName()
|
|
self.parent = None
|
|
try:
|
|
self.source = self.dev.getChannelName()
|
|
except:
|
|
self.source = "Unknown"
|
|
try:
|
|
try:
|
|
self.shape = [self.dev.getHeight(), self.dev.getWidth()]
|
|
self.shape_str = "["+str(self.shape[0])+"]"+"["+str(self.shape[1])+"]"
|
|
except:
|
|
self.shape = [self.dev.getSize()]
|
|
self.shape_str = "["+str(self.shape[0])+"]"
|
|
except:
|
|
self.shape = []
|
|
self.shape_str=""
|
|
try:
|
|
self.precision = self.dev.getPrecision()
|
|
except:
|
|
self.precision = None
|
|
self.dtype = 'number'
|
|
self.cfg_description = {"shape_str":{"dtype":"string", 'shape':(len(self.shape_str),), "source":""}, }
|
|
self.configuration = {"shape_str":{"value":self.shape_str, "timestamp":time.time()}}
|
|
self.name = self.name+self.shape_str
|
|
|
|
def read(self):
|
|
if is_main_thread():
|
|
return {self.name:{"value":self.dev.read(), "timestamp":time.time()}}
|
|
global __return__,__exception__
|
|
__return__ = __exception__ = None
|
|
handler.queue.append(("read", (self.dev)))
|
|
while (__return__ is None) and (__exception__ is None):
|
|
time.sleep(0.01)
|
|
if __exception__ is not None:
|
|
raise __exception__
|
|
return {self.name:{"value":__return__, "timestamp":time.time()}}
|
|
|
|
def describe(self):
|
|
self.description = {self.name: { \
|
|
'dtype':self.dtype , \
|
|
'shape': self.shape, \
|
|
'source': self.source, \
|
|
'precision': self.precision \
|
|
}}
|
|
return self.description
|
|
|
|
def describe_configuration(self):
|
|
return self.cfg_description
|
|
|
|
def read_configuration(self):
|
|
return self.configuration
|
|
|
|
class NullStatus:
|
|
"a simple Status object that is always immediately done"
|
|
def __init__(self):
|
|
self._cb = None
|
|
self.done = True
|
|
self.success = True
|
|
|
|
@property
|
|
def finished_cb(self):
|
|
return self._cb
|
|
|
|
@finished_cb.setter
|
|
def finished_cb(self, cb):
|
|
cb()
|
|
self._cb = cb
|
|
|
|
class MoverWrapper(ReaderWrapper):
|
|
def set(self, value):
|
|
if is_main_thread():
|
|
self.dev.write(value)
|
|
else:
|
|
global __return__,__exception__
|
|
__return__ = __exception__ = None
|
|
handler.queue.append(("write", (self.dev, value)))
|
|
while (__return__ is None) and (__exception__ is None):
|
|
time.sleep(0.01)
|
|
if __exception__ is not None:
|
|
raise __exception__
|
|
self.dev.waitReady(-1)
|
|
return NullStatus()
|
|
|
|
@property
|
|
def position(self):
|
|
return self.dev.getPosition()
|
|
|
|
def stop(self, *, success=False):
|
|
self.dev.stop()
|
|
|
|
|
|
#Wraps an Ophyd device as a Readable, so can be used in *scan commands
|
|
class Ophyd(Readable):
|
|
def __init__(self, dev):
|
|
for k in dev.describe().keys():
|
|
Nameable.__init__(self, k, Readable.__interfaces__ )
|
|
break
|
|
self.dev=dev
|
|
|
|
def read(self):
|
|
v = self.dev.read()
|
|
if (v is None) or (len(v)==0):
|
|
return None
|
|
v = v[self.getName()]
|
|
try:
|
|
timestamp = int(v["timestamp"]*1000)
|
|
except:
|
|
timestamp = time.time()
|
|
ret = TimestampedValue (v["value"], timestamp)
|
|
return ret
|
|
|
|
|
|
RE = RunEngine({}, during_task=EventProcessingTask())
|
|
if CAN_PAUSE:
|
|
RE.pause_msg="User abort"
|
|
bec = BestEffortCallback()
|
|
bec.disable_plots()
|
|
RE.subscribe(bec)
|
|
RE.subscribe(handler)
|
|
|
|
motor.delay = 1.1 # simulate slow motor movement
|
|
ch1 = EpicsSignal("TESTIOC:TESTSINUS:SinCalc")
|
|
#TODO: DEmonstrate use of waveform and areadetector (Manual scan setup with the indexes in name)
|
|
#ch2 = EpicsSignal("TESTIOC:TESTWF2:MyWF", name="arr[10]")
|
|
#ch3 = EpicsSignal("TESTIOC:TESTWF2:MyWF", name="img[3][2]")det3=ReaderWrapper(sin)
|
|
dets = [det1, det2] |