Files
dev/script/cpy/local.py
2024-06-10 10:44:16 +02:00

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]