Files
ncs/script/Lib_old/startup.py
boccioli_m 104d203536 Startup
2015-08-28 17:35:28 +02:00

798 lines
28 KiB
Python

################################################################################
# Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
################################################################################
import sys
import time
import math
import os.path
from array import array
import jarray
import java.lang.Class as Class
import java.lang.Object as Object
import java.beans.PropertyChangeListener
import java.util.concurrent.Callable
import java.util.List
import java.lang.reflect.Array
import org.python.core.PyArray as PyArray
import ch.psi.utils.Threading as Threading
import ch.psi.utils.State as State
import ch.psi.utils.Convert as Convert
import ch.psi.pshell.core.Controller.CommandSource as CommandSource
import ch.psi.pshell.data.PlotDescriptor as PlotDescriptor
import ch.psi.pshell.dev.Readable as Readable
import ch.psi.pshell.dev.Writable as Writable
import ch.psi.pshell.dev.DeviceAdapter as DeviceAdapter
import ch.psi.pshell.dev.DeviceListener as DeviceListener
import ch.psi.pshell.dev.MotorGroupBase.MoveMode as MoveMode
import ch.psi.pshell.epics.Epics as Epics
import ch.psi.pshell.epics.EpicsScan as EpicsScan
import ch.psi.pshell.scan.LineScan
import ch.psi.pshell.scan.AreaScan
import ch.psi.pshell.scan.VectorScan
import ch.psi.pshell.scan.ManualScan
################################################################################
#Scan functions
################################################################################
def onScanBeforeReadout(scan):
try:
if (scan.before_read!=None):
scan.before_read()
except AttributeError:
pass
def onScanAfterReadout(scan):
try:
if (scan.after_read!=None):
scan.after_read()
except AttributeError:
pass
class LineScan(ch.psi.pshell.scan.LineScan):
def onBeforeReadout(self):
onScanBeforeReadout(self)
def onAfterReadout(self):
onScanAfterReadout(self)
class AreaScan(ch.psi.pshell.scan.AreaScan):
def onBeforeReadout(self):
onScanBeforeReadout(self)
def onAfterReadout(self):
onScanAfterReadout(self)
class VectorScan(ch.psi.pshell.scan.VectorScan):
def onBeforeReadout(self):
onScanBeforeReadout(self)
def onAfterReadout(self):
onScanAfterReadout(self)
class ManualScan (ch.psi.pshell.scan.ManualScan):
def __init__(self, writables, readables, start = None, end = None, steps = None, relative = False):
ch.psi.pshell.scan.ManualScan.__init__(self, writables, readables, start, end, steps, relative, controller)
def append(self,setpoints, positions, values):
ch.psi.pshell.scan.ManualScan.append(self, to_object_array(setpoints), to_object_array(positions), to_object_array(values))
def sleep(sec):
"""Sleep.
Args:
sec(float): Time in seconds.
"""
time.sleep(sec)
def to_list(obj):
if isinstance(obj,tuple):
return list(obj)
if not isinstance(obj,list):
return [obj,]
return obj
def is_list(obj):
return isinstance(obj,tuple) or isinstance(obj,list)
class_types = {
'b': "java.lang.Byte",
'h': "java.lang.Short",
'u': "java.lang.Integer",
'i': "java.lang.Integer",
'l': "java.lang.Long",
'c': "java.lang.Character",
'f': "java.lang.Float",
'd': "java.lang.Double",
's': "java.lang.String",
'o': "java.lang.Object",
'[b': "[B", #byte
'[h': "[S", #short
'[u': "[I", #int
'[i': "[I", #int
'[l': "[J", #long
'[c': "[C", #char
'[f': "[F", #float
'[d': "[D", #double
'[s': "[Ljava.lang.String;",
'[o': "[Ljava.lang.Object;",
}
def to_array(obj, type):
"""Convert Python list to Java array.
Args:
obj(list): Original data.
type(str): array type 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float, 'd' = double,
'c' = char, 's' = String, 'o' = Object
Returns:
Java array.
"""
if type[0] == '[':
type = type[1:]
arrayType = class_types.get("["+type)
if obj is None:
return None
if isinstance(obj,java.util.List):
obj = obj.toArray(java.lang.reflect.Array.newInstance(Class.forName(class_types.get(type)),0))
obj = Convert.wrapperArrayToPrimitiveArray(obj)
if isinstance(obj,PyArray):
return obj
if is_list(obj):
if type=='o' or type== 's':
ret = java.lang.reflect.Array.newInstance(Class.forName(class_types.get(type)),len(obj))
for i in range (len(obj)):
if is_list(obj):
ret[i] = to_array(obj[i],type)
elif type == 's':
ret[i] = str(obj[i])
else:
ret[i] = obj[i]
return ret
if len(obj)>0 and is_list(obj[0]):
if len(obj[0])>0 and is_list(obj[0][0]):
ret = java.lang.reflect.Array.newInstance(Class.forName(arrayType),len(obj),len(obj[0]))
for i in range(len(obj)):
ret[i]=to_array(obj[i], type)
return ret
else:
ret = java.lang.reflect.Array.newInstance(Class.forName(arrayType),len(obj))
for i in range(len(obj)):
ret[i]=to_array(obj[i], type)
return ret
return jarray.array(obj,type)
return obj
def to_double_array(obj):
return to_array(obj, 'd')
def to_float_array(obj):
return to_array(obj, 'f')
def to_long_array(obj):
return to_array(obj, 'l')
def to_int_array(obj):
return to_array(obj, 'i')
def to_short_array(obj):
return to_array(obj, 'h')
def to_byte_array(obj):
return to_array(obj, 'b')
def to_string_array(obj):
return to_array(obj, 's')
def to_object_array(obj):
return to_array(obj, 'o')
def lscan(writables, readables, start, end, steps, latency=0.0, relative = False, context=None, before_read=None, after_read=None):
"""Line Scan: positioners change together, linearly from start to end positions.
Args:
writables(list of Writable): Positioners set on each step.
readables(list of Readable): Sensors to be sampled on each step.
start(list of float): start positions of writables.
end(list of float): final positions of writables.
steps(int or float or list of float): number of scan steps (int) or step size (float).
relative (bool, optional): if true, start and end positions are relative to current at start of the scan
latency(float, optional): sleep time in each step before readout, defaults to 0.0.
context(str, optional): plotting context name.
before_read (function): callback on each step, before each readout.
after_read (function): callback on each step, after each readout.
Returns:
ArrayList of ScanRecord objects.
"""
latency_ms=int(latency*1000)
writables=to_list(writables)
readables=to_list(readables)
start=to_list(start)
end=to_list(end)
scan = LineScan(writables,readables, start, end , steps, relative, latency_ms, controller)
scan.before_read=before_read
scan.after_read=after_read
scan.setPlotName(context)
scan.start()
return scan.getResult()
def vscan(writables, readables, vector, latency=0.0, relative = False, context=None, before_read=None, after_read=None):
"""Vector Scan: positioners change following values provided in a vector.
Args:
writables(list of Writable): Positioners set on each step.
readables(list of Readable): Sensors to be sampled on each step.
vector(list of list of float): table of positioner values.
relative (bool, optional): if true, start and end positions are relative to current at start of the scan
latency(float, optional): sleep time in each step before readout, defaults to 0.0.
context(str, optional): plotting context name.
before_read (function): callback on each step, before each readout.
after_read (function): callback on each step, after each readout.
Returns:
ArrayList of ScanRecord objects.
"""
latency_ms=int(latency*1000)
writables=to_list(writables)
readables=to_list(readables)
scan = VectorScan(writables,readables, vector, relative, latency_ms, controller)
scan.before_read=before_read
scan.after_read=after_read
scan.setPlotName(context)
scan.start()
return scan.getResult()
def ascan(writables, readables, start, end, steps, latency=0.0, relative = False, context=None, before_read=None, after_read=None):
"""Area Scan: multi-dimentional scan, each positioner is a dimention.
Args:
writables(list of Writable): Positioners set on each step.
readables(list of Readable): Sensors to be sampled on each step.
start(list of float): start positions of writables.
end(list of float): final positions of writables.
steps(list of int or list of float): number of scan steps (int) or step size (float).
relative (bool, optional): if true, start and end positions are relative to current at start of the scan
latency(float, optional): sleep time in each step before readout, defaults to 0.0.
context(str, optional): plotting context name.
before_read (function): callback on each step, before each readout.
after_read (function): callback on each step, after each readout.
Returns:
ArrayList of ScanRecord objects.
"""
latency_ms=int(latency*1000)
writables=to_list(writables)
readables=to_list(readables)
start=to_list(start)
end=to_list(end)
scan = AreaScan(writables,readables, start, end , steps, relative, latency_ms, controller)
scan.before_read=before_read
scan.after_read=after_read
scan.setPlotName(context)
scan.start()
return scan.getResult()
def tscan(readables, points, interval, context=None, before_read=None, after_read=None):
"""Time Scan: sensors are sampled in fixed time intervals.
Args:
readables(list of Readable): Sensors to be sampled on each step.
points(int): number of samples.
interval(float): time interval between readouts.
context(str, optional): plotting context name.
before_read (function): callback on each step, before each readout.
after_read (function): callback on each step, after each readout.
Returns:
ArrayList of ScanRecord objects.
"""
latency_ms=int(interval*1000)
writables=[]
readables=to_list(readables)
start=[0,]
end=[points,]
steps=points
scan = LineScan(writables,readables, start, end , steps, False, latency_ms, controller)
scan.before_read=before_read
scan.after_read=after_read
scan.setPlotName(context)
scan.start()
return scan.getResult()
def escan(name, context=None):
"""Epics Scan: execute an Epics Scan Record.
Args:
name(str): Name of scan record.
context(str, optional): plotting context name.
Returns:
ArrayList of ScanRecord objects.
"""
scan = EpicsScan(name, controller)
scan.setPlotName(context)
scan.start()
return scan.getResult()
################################################################################
#Plot functions
################################################################################
def plot(data, name = None, xdata = None, ydata=None, context=None):
"""Request one or multiple plots of user data (1d, 2d or 3d)
Args:
data: array or list of values. For multiple plots, array of arrays or lists of values.
name(str or list of str, optional): Plot name or list of names (if multiple plots).
xdata: array or list of values. For multiple plots, array of arrays or lists of values.
ydata: array or list of values. For multiple plots, array of arrays or lists of values.
context(str, optional): plotting context name.
Returns:
ArrayList of Plot objects.
"""
if (name is not None) and is_list(name):
if len(name)==0:
name=None;
plots = java.lang.reflect.Array.newInstance(Class.forName("ch.psi.pshell.data.PlotDescriptor"), len(data))
for i in range (len(data)):
plotName = None if (name is None) else name[i]
x = xdata
if is_list(x) and len(x)>0 and (is_list(x[0]) or isinstance(x[0] , java.util.List)):
x = x[i]
y = ydata
if is_list(y) and len(y)>0 and (is_list(y[0]) or isinstance(y[0] , java.util.List)):
y = y[i]
plots[i] = PlotDescriptor(plotName , to_double_array(data[i]), to_double_array(x), to_double_array(y))
return controller.plot(plots,context)
else:
plot = PlotDescriptor(name, to_double_array(data), to_double_array(xdata), to_double_array(ydata))
return controller.plot(plot,context)
################################################################################
#Data access functions
################################################################################
def load_data(path, page=0):
slice = controller.dataManager.getData(path, page)
return slice.sliceData
def get_attributes(path):
return controller.dataManager.getAttributes(path)
def save_dataset(path, data):
data = to_double_array(data)
controller.dataManager.setDataset(path,data)
def create_dataset(path, type, unsigned=False, dimensions=None):
type = Class.forName(class_types.get(type,type))
controller.dataManager.createDataset(path, type, unsigned, dimensions)
def create_table(path, names, types=None, lengths=None, dims=None):
if (types is not None):
for i in range (len(types)):
types[i] = Class.forName(class_types.get(types[i],types[i]))
controller.dataManager.createDataset(path, names,types, lengths,dims)
def save_data_item(path, data, index=None):
data = to_double_array(data)
if index is None:
controller.dataManager.appendItem(path, data)
else:
controller.dataManager.setItem(path, data, index)
def save_table_item(path, data):
#data = to_object_array(data)
if is_list(data):
arr = java.lang.reflect.Array.newInstance(Class.forName("java.lang.Object"),len(data))
for i in range (len(data)):
if is_list(data[i]):
arr[i] = to_double_array(data[i])
else:
arr[i] = data[i]
data=arr
controller.dataManager.appendItem(path, data)
def flush_data():
controller.dataManager.flush()
def set_attribute(path, name, value, unsigned = False):
controller.dataManager.setAttribute(path,name, value, unsigned)
def get_current_data_group():
return controller.dataManager.getCurrentGroup()
def set_flush_records(value):
"""Override, for the executing scripot, the configuration option to flush each record.
"""
controller.dataManager.flushRecords = value
################################################################################
#Epics access functions
################################################################################
channel_types = {
'b': "java.lang.Byte",
'i': "java.lang.Short",
'l': "java.lang.Integer",
'f': "java.lang.Float",
'd': "java.lang.Double",
's': "java.lang.String",
'[b': "[B",
'[i': "[S",
'[l': "[I",
'[f': "[F",
'[d': "[D",
'[s': "[Ljava.lang.String;",
}
def caget(name, type='s', size=None):
"""Reads an Epics PV.
Args:
name(str): PV name
type(str, optional): type of PV, defaults 'd'.
Scalar values: 'b', 'i', 'l', 'd', 's'.
Array: values: '[b', '[i,', '[l', '[d', '[s'.
size (int, optional): for arrays, number of elements to be read. Default read all.
Returns:
PV value
"""
return Epics.get(name, Class.forName(channel_types.get(type,type)),size)
def cawait(name, value, timeout=None, comparator=None, type='s', size=None):
"""Wait for a PV to have a given value.
Args:
name(str): PV name
value (obj): value to compare to
timeout(float, optional): time in seconds to wait. If None, waits forever.
comparator(java.util.Comparator, optional): if None, equality.
type(str, optional): type of PV, defaults 'd'.
Scalar values: 'b', 'i', 'l', 'd', 's'.
Array: values: '[b', '[i,', '[l', '[d', '[s'.
size (int, optional): for arrays, number of elements to be read. Default read all.
"""
if (timeout is not None):
timeout = int(timeout*1000)
value = adjust_value(value)
Epics.waitValue(name, value, comparator, timeout, Class.forName(channel_types.get(type,type)),size)
def adjust_value(value, var_type=None):
if (value is None):
return value
if (var_type is not None):
if is_list(value):
var_type = var_type.replace(',','').replace('[','')
ret = []
for item in value:
ret.append(adjust_value(item), var_type)
value = ret
else:
var_type = var_type.lower()
if var_type=='b':
value = byte(value)
elif var_type=='i':
value = short(value)
elif var_type=='l':
value = int(value)
elif var_type=='f':
value = float(value)
elif var_type=='d':
value = float(value)
elif var_type=='s':
value = str(value)
if isinstance(value,tuple):
value = list(value)
if isinstance(value,list):
list_type = type(value[0])
array_types = {
int: "i",
long: "l",
float:"d",
str:Class.forName("java.lang.String"),
}
array_type = array_types.get(type(value[0]),'d')
array = PyArray(array_type)
array.fromlist(value)
value=array
return value
def caput(name, value, timeout = None):
"""Writes to an Epics PV.
Args:
name(str): PV name
value(scalar, string or array): new PV value.
timeout(int, optional): timeout in seconds to the write. If None waits forever to completion.
"""
value=adjust_value(value)
if (timeout is not None):
timeout = int(timeout*1000)
return Epics.put(name, value, timeout)
def caputq(name, value):
"""Writes to an Epics PV and does not wait.
Args:
name(str): PV name
value(scalar, string or array): new PV value.
"""
value=adjust_value(value)
return Epics.putq(name, value)
def create_channel_device(channelName, type = None, size = None, deviceName = None):
if type is None:
type = 's'
type = Class.forName(channel_types.get(type,type))
dev = Epics.newChannelDevice(deviceName, channelName, type)
dev.initialize()
if (size is not None):
dev.setSize(size)
return dev
def create_channel(name, type = None, size = None):
if type is None:
type = 's'
type = Class.forName(channel_types.get(type,type))
return Epics.newChannel(name, type, size)
class Channel(java.beans.PropertyChangeListener):
def __init__(self, name, type = None, size = None, callback=None):
self.channel = create_channel(name, type, size)
self.callback = callback
def get_name(self):
return self.channel.name
def get_size(self):
return self.channel.size
def set_size(self, size):
self.channel.size = size
def is_connected(self):
return self.channel.connected
def is_monitored(self):
return self.channel.monitored
def set_monitored(self, value):
self.channel.monitored = value
if (value):
self.channel.addPropertyChangeListener(self)
else:
self.channel.removePropertyChangeListener(self)
def propertyChange(self, pce):
if pce.getPropertyName() == "value":
if self.callback is not None:
self.callback(pce.getNewValue())
def put(self, value, timeout=None):
if (timeout==None):
self.channel.setValue(value)
else:
self.channel.setValueAsync(value).get(int(timeout*1000), java.util.concurrent.TimeUnit.MILLISECONDS);
def putq(self, value):
self.channel.setValueNoWait(value)
def get(self):
return self.channel.getValue()
def wait_for_value(self, value, timeout=None, comparator=None):
if comparator is None:
if timeout is None:
self.channel.waitForValue(value)
else:
self.channel.waitForValue(value, int(timeout*1000))
else:
if timeout is None:
self.channel.waitForValue(value, comparator)
else:
self.channel.waitForValue(value, comparator, int(timeout*1000))
def close(self):
self.channel.destroy()
################################################################################
#Concurrent execution
################################################################################
class Callable(java.util.concurrent.Callable):
def __init__(self, method, *args):
self.method = method
self.args = args
def call(self):
#try:
return self.method(*self.args)
#except:
# traceback.print_exc(file=sys.stderr)
def fork(*methods):
"""Start execution of methods in parallel.
Args:
*methods(method references)
Returns:
List of callable objects
"""
callables = []
for m in methods:
if is_list(m):
callables.append(Callable(m[0],*m[1]))
else:
callables.append(Callable(m))
return Threading.fork(callables)
def join(callables):
"""Wait parallel execution of methods.
Args:
callables(list of Callables) : as returned from fork
"""
try:
return Threading.join(callables)
except java.util.concurrent.ExecutionException as ex:
raise ex.getCause()
def parallelize(*methods):
"""Equivalent to fork + join
Args:
*methods(method references)
"""
callables = fork(*methods)
return join(callables)
################################################################################
#Background tasks control
################################################################################
def startTask(script, delay = 0.0, interval = -1):
"""Start a background task
Args:
script(str): Name of the script implementing the task
delay(float, optional): time in seconds for the first execution. Default starts immediately.
interval(float, optional): time in seconds for between execution. If negative (default), single-execution.
"""
delay_ms=int(delay*1000)
interval_ms=int(interval*1000) if (interval>=0) else int(interval)
controller.taskManager.create(script, delay_ms, interval_ms)
controller.taskManager.start(script)
def stopTask(script, force = False):
"""Stop a background task
Args:
script(str): Name of the script implementing the task
force(boolean, optional): interrupt current execution, if running
"""
controller.taskManager.remove(script, force)
################################################################################
#Logging
################################################################################
def log(log):
"""Writes log to data file.
Args:
log(str): Log string
"""
controller.dataManager.appendLog(str(log))
################################################################################
#Standard Libraries
################################################################################
ca_channel_path=os.path.join(controller.setup.getStandardLibraryPath(), "epics")
sys.path.append(ca_channel_path)
#This is to destroy previous context of _ca (it is not shared with PShell)
if run_count > 0:
if sys.modules.has_key("_ca"):
import _ca
_ca.initialize()
################################################################################
#Utilities
################################################################################
#Float range -> Useful for scanning is a range
def frange_gen(start, finish, step):
while ((step >= 0.0) and (start <= finish)) or ((step < 0.0) and (start >= finish)):
yield start
start += step
def frange(start, finish, step, enforce_finish = False, inclusive_finish = False):
"""Create a list with a range of float values
"""
ret = list(frange_gen(start, finish, step))
if len(ret) > 0:
if inclusive_finish == False:
if ret[-1]==finish:
del ret[-1]
if enforce_finish and ret[-1]!=finish:
ret.append(finish)
return ret
def set_status(status):
set_preference("status", status)
def set_preference(name, value):
"""Hints to graphical layer:
Args:
name(str): Preference name
- "scanPlotDisabled": enable/disable scan plot (True/False)
- "scanTableDisabled": enable/disable scan table (True/False)
- "enabledPlots": select Readables to be plotted (list of Readable or String (Readable names))
- "status": set application status
value(object): preference valye
"""
value = to_array(value, 'o') #If list then convert to Object array
controller.setPreference(CommandSource.script, name, value)
def inject_vars():
"""Re-inject devices and interpreter variables
"""
controller.injectVars(CommandSource.script)
def run(script_name, globals = globals(), locals = locals()):
"""Run script: can be absolute path, relative, or short name to be search in the path.
"""
global _
script = controller.scriptManager.library.resolveFile(script_name)
if script is not None and os.path.isfile(script):
_ = None
execfile(script, globals, locals)
return _
#print >> sys.stderr, "Invalid script: " + str(script_name)
raise IOError("Invalid script: " + str(script_name))
def set_return(value):
global _
#In Jython, the output of last statement is not returned when running a file so we have to use a return variable
_=value #Used when running file
return value #Used when parsing file
################################################################################
#Local
################################################################################
try:
run("local")
except IOError: #If file not present ignore exception
pass