1422 lines
47 KiB
Groovy
1422 lines
47 KiB
Groovy
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Global definitions and built-in functions
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
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.util.Map
|
|
import java.lang.reflect.Array
|
|
|
|
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.Context
|
|
import ch.psi.pshell.data.PlotDescriptor as PlotDescriptor
|
|
import ch.psi.pshell.device.Device as Device
|
|
import ch.psi.pshell.device.DeviceBase as DeviceBase
|
|
import ch.psi.pshell.device.Interlock as Interlock
|
|
import ch.psi.pshell.device.Readable as Readable
|
|
import ch.psi.pshell.device.Readable.ReadableArray as ReadableArray
|
|
import ch.psi.pshell.device.Readable.ReadableMatrix as ReadableMatrix
|
|
import ch.psi.pshell.device.Writable as Writable
|
|
import ch.psi.pshell.device.DeviceListener as DeviceListener
|
|
import ch.psi.pshell.device.MoveMode as MoveMode
|
|
import ch.psi.pshell.epics.Epics as Epics
|
|
import ch.psi.pshell.epics.EpicsScan as EpicsScan
|
|
import ch.psi.pshell.imaging.Source as Source
|
|
import ch.psi.pshell.imaging.SourceBase as SourceBase
|
|
import ch.psi.pshell.plot.LinePlotSeries as LinePlotSeries
|
|
import ch.psi.pshell.plot.MatrixPlotSeries as MatrixPlotSeries
|
|
import ch.psi.pshell.scan.LineScan
|
|
import ch.psi.pshell.scan.ContinuousScan
|
|
import ch.psi.pshell.scan.AreaScan
|
|
import ch.psi.pshell.scan.VectorScan
|
|
import ch.psi.pshell.scan.ManualScan
|
|
import ch.psi.pshell.scan.RegionScan
|
|
import ch.psi.pshell.scan.HardwareScan
|
|
import ch.psi.pshell.scan.ScanRecord
|
|
import ch.psi.pshell.scan.TimeScan
|
|
import ch.psi.pshell.bs.BsScan
|
|
import ch.psi.pshell.bs.Stream
|
|
import ch.psi.pshell.scripting.ViewPreference as Preference
|
|
import ch.psi.pshell.scripting.ScriptUtils as ScriptUtils
|
|
|
|
|
|
def get_context(){
|
|
return ch.psi.pshell.core.Context.getInstance();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Type conversion and checking
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def get_rank(obj){
|
|
def rank = 0;
|
|
|
|
while (obj != null) {
|
|
Class type = obj.getClass();
|
|
if (type.isArray()){
|
|
while (type.isArray()) {
|
|
rank++;
|
|
type = type.getComponentType();
|
|
}
|
|
return rank
|
|
} else if (obj instanceof List){
|
|
while (obj instanceof List){
|
|
rank++;
|
|
if (((List)obj).size() == 0) return rank;
|
|
obj = obj.get(0);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rank;
|
|
}
|
|
|
|
def get_length(obj){
|
|
if (obj == null) return 0
|
|
if (obj instanceof List) return obj.size()
|
|
if (obj.getClass().isArray()) return obj.length
|
|
return 1
|
|
}
|
|
|
|
def to_array(obj, type){
|
|
if (obj==null) {
|
|
return null;
|
|
}
|
|
|
|
def rank = get_rank(obj);
|
|
if (rank == 0) {
|
|
rank = 1;
|
|
obj = [obj]
|
|
}
|
|
def size = get_length(obj)
|
|
//Custom class
|
|
if (!ScriptUtils.isStandardType(type)) {
|
|
def ret = java.lang.reflect.Array.newInstance(java.lang.Class.forName(type), size)
|
|
for (def i = 0; i < size; i++) {
|
|
ret[i] = obj[i]
|
|
}
|
|
return ret;
|
|
}
|
|
def name = ScriptUtils.getType(type).getName()
|
|
if (type[0] != "[") {
|
|
def array_type = "[" + type;
|
|
name = ScriptUtils.getType(array_type).getName()
|
|
for (def i = 1; i < rank; i++) {
|
|
name = "[" + name;
|
|
}
|
|
}
|
|
def ret
|
|
switch(name) {
|
|
case "[B": ret = new byte[size] ; break
|
|
case "[S": ret = new short[size] ; break
|
|
case "[I": ret = new int[size] ; break
|
|
case "[J": ret = new long[size] ; break
|
|
case "[C": ret = new char[size] ; break
|
|
case "[F": ret = new float[size] ; break
|
|
case "[D": ret = new double[size] ; break
|
|
default:
|
|
ret = java.lang.reflect.Array.newInstance(java.lang.Class.forName( name [1..-1]), size)//Remove first array marker because Array.newInstance will create an array of type
|
|
}
|
|
|
|
for (def i = 0; i < size; i++) {
|
|
if (obj[i] instanceof List || obj[i].getClass().isArray()){
|
|
ret[i] = to_array(obj[i],type)
|
|
} else {
|
|
ret[i] = obj[i]
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
def to_list(array){
|
|
return Arrays.asList(array)
|
|
}
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Standard scan commands
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
def lscan(writables, readables, start, end, steps, latency=0.0, relative=false, passes=1, zigzag =false, Closure before_read=null, Closure after_read=null, title=null) {
|
|
/*
|
|
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.
|
|
passes(int, optional): number of passes
|
|
zigzag(bool, optional): if true writables invert direction on each pass.
|
|
before_read (function): callback on each step, before each readout. Callback may have as
|
|
optional parameters list of positions.
|
|
after_read (function): callback on each step, after each readout. Callback may have as
|
|
optional parameters a ScanRecord object.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
*/
|
|
def int latency_ms = latency * 1000
|
|
writables = to_array(string_to_obj(writables), "ch.psi.pshell.device.Writable")
|
|
readables = to_array(string_to_obj(readables), "ch.psi.pshell.device.Readable")
|
|
start = to_array(start, 'd')
|
|
end = to_array(end, 'd')
|
|
steps = get_rank(steps)>0 ? to_array(steps, steps[0].getClass()==java.lang.Integer ? 'i' : 'd') : (int)steps
|
|
def scan = new LineScan(writables, readables, start, end, steps, relative, (int)latency_ms, passes, zigzag) {
|
|
protected void onBeforeReadout(double[] pos) {
|
|
if (before_read!=null) before_read(pos)
|
|
}
|
|
protected void onAfterReadout(ScanRecord record) {
|
|
if (after_read!=null) after_read(record)
|
|
}
|
|
}
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
}
|
|
|
|
def vscan(writables, readables, vector, line, latency=0.0, relative=false, passes=1, zigzag=false, Closure before_read=null, Closure after_read=null, title=null) {
|
|
/*
|
|
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.
|
|
line (bool, optional): if true, processs as line scan (1d)
|
|
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.
|
|
passes(int, optional): number of passes
|
|
zigzag(bool, optional): if true writables invert direction on each pass.
|
|
before_read (function): callback on each step, before each readout.
|
|
after_read (function): callback on each step, after each readout.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
*/
|
|
def int latency_ms = latency * 1000
|
|
writables = to_array(string_to_obj(writables), "ch.psi.pshell.device.Writable")
|
|
readables = to_array(string_to_obj(readables), "ch.psi.pshell.device.Readable")
|
|
if (get_length(vector) == 0)
|
|
vector.append([])
|
|
vector = to_array(vector, 'd')
|
|
def scan = new VectorScan(writables, readables, vector, line, relative, (int)latency_ms, passes, zigzag) {
|
|
protected void onBeforeReadout(double[] pos) {
|
|
if (before_read!=null) before_read(pos)
|
|
}
|
|
protected void onAfterReadout(ScanRecord record) {
|
|
if (after_read!=null) after_read(record)
|
|
}
|
|
}
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
}
|
|
|
|
def ascan(writables, readables, start, end, steps, latency=0.0, relative=false, passes=1, zigzag=false, Closure before_read=null, Closure after_read=null, title=null) {
|
|
/*
|
|
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.
|
|
passes(int, optional): number of passes
|
|
zigzag (bool, optional): if true writables invert direction on each row.
|
|
before_read (function): callback on each step, before each readout.
|
|
after_read (function): callback on each step, after each readout.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
*/
|
|
|
|
def int latency_ms = latency * 1000
|
|
writables = to_array(string_to_obj(writables), "ch.psi.pshell.device.Writable")
|
|
readables = to_array(string_to_obj(readables), "ch.psi.pshell.device.Readable")
|
|
start = to_array(start, 'd')
|
|
end = to_array(end, 'd')
|
|
steps = to_array(steps, steps[0].getClass()==java.lang.Integer ? 'i' : 'd')
|
|
|
|
def scan = new AreaScan(writables, readables, start, end, steps, relative, (int)latency_ms, passes, zigzag) {
|
|
protected void onBeforeReadout(double[] pos) {
|
|
if (before_read!=null) before_read(pos)
|
|
}
|
|
protected void onAfterReadout(ScanRecord record) {
|
|
if (after_read!=null) after_read(record)
|
|
}
|
|
}
|
|
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
}
|
|
|
|
def rscan(writable, readables, regions, latency=0.0, relative=false, passes=1, zigzag=false, Closure before_read=null, Closure after_read=null, title=null){
|
|
/*
|
|
Region Scan: positioner scanned linearly, from start to end positions, in multiple regions.
|
|
|
|
Args:
|
|
writable(Writable): Positioner set on each step, for each region.
|
|
readables(list of Readable): Sensors to be sampled on each step.
|
|
regions (list of tuples (float,floar, int) or (float,floar, float)): each tuple define a scan region
|
|
(start, stop, steps) or (start, stop, step_size)
|
|
relative (bool, optional): if true, start and end positions are relative to
|
|
current at start of the scan
|
|
latency(float, optional): settling time for each step before readout, defaults to 0.0.
|
|
passes(int, optional): number of passes
|
|
zigzag(bool, optional): if true writable invert direction on each pass.
|
|
before_read (function): callback on each step, before each readout. Callback may have as
|
|
optional parameters list of positions.
|
|
after_read (function): callback on each step, after each readout. Callback may have as
|
|
optional parameters a ScanRecord object.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
*/
|
|
|
|
def int latency_ms = latency * 1000
|
|
writable = string_to_obj(writable)
|
|
readables = to_array(string_to_obj(readables), "ch.psi.pshell.device.Readable")
|
|
start=[]
|
|
end=[]
|
|
steps=[]
|
|
for (def region in regions) {
|
|
start.add(region[0])
|
|
end.add(region[1])
|
|
steps.add(region[2])
|
|
}
|
|
start = to_array(start, 'd')
|
|
end = to_array(end, 'd')
|
|
steps = to_array(steps, steps[0].getClass()==java.lang.Integer ? 'i' : 'd')
|
|
|
|
def scan = new RegionScan(writable, readables, start, end, steps, relative, (int)latency_ms, passes, zigzag) {
|
|
protected void onBeforeReadout(double[] pos) {
|
|
if (before_read!=null) before_read(pos)
|
|
}
|
|
protected void onAfterReadout(ScanRecord record) {
|
|
if (after_read!=null) after_read(record)
|
|
}
|
|
}
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
}
|
|
|
|
|
|
def cscan(writables, readables, start, end, steps,latency=0.0, time=null, relative=false, passes=1, zigzag=false, Closure before_read=null, Closure after_read=null, title=null) {
|
|
/*
|
|
Continuous Scan: positioner change continuously from start to end position and readables are sampled on the fly.
|
|
|
|
Args:
|
|
writablse(Speedable or list of Motor): A positioner with a getSpeed method or
|
|
a list of motors.
|
|
readables(list of Readable): Sensors to be sampled on each step.
|
|
start(float or list of float): start positions of writables.
|
|
end(float or list of float): final positions of writabless.
|
|
steps(int or float or list of float): number of scan steps (int) or step size (float).
|
|
time = null
|
|
time (float, seconds): if not null then writables is Motor array and speeds are
|
|
set according to time.
|
|
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.
|
|
before_read (function): callback on each step, before each readout.
|
|
Callback may have as optional parameters list of positions.
|
|
after_read (function): callback on each step, after each readout.
|
|
Callback may have as optional parameters a ScanRecord object.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
|
|
*/
|
|
def int latency_ms = latency * 1000
|
|
writables = to_array(string_to_obj(writables), "ch.psi.pshell.device.Writable")
|
|
readables = to_array(string_to_obj(readables), "ch.psi.pshell.device.Readable")
|
|
start = to_array(start, 'd')
|
|
end = to_array(end, 'd')
|
|
if (get_rank(steps)>0){
|
|
steps = to_array(steps, steps[0].getClass()==java.lang.Integer ? 'i' : 'd')
|
|
} else {
|
|
steps = to_array(steps, steps.getClass()==java.lang.Integer ? 'i' : 'd')
|
|
}
|
|
//A single Writable with fixed speed
|
|
def scan
|
|
if (time == null) {
|
|
scan = new ContinuousScan(writables[0], readables, start[0], end[0], steps[0], relative, (int)latency_ms, passes, zigzag) {
|
|
protected void onBeforeReadout(double[] pos) {
|
|
if (before_read!=null) before_read(pos)
|
|
}
|
|
protected void onAfterReadout(ScanRecord record) {
|
|
if (after_read!=null) after_read(record)
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//A set of Writables with speed configurable
|
|
scan = new ContinuousScan(writables, readables, start, end, steps, time, relative, (int)latency_ms, passes, zigzag) {
|
|
protected void onBeforeReadout(double[] pos) {
|
|
if (before_read!=null) before_read(pos)
|
|
}
|
|
protected void onAfterReadout(ScanRecord record) {
|
|
if (after_read!=null) after_read(record)
|
|
}
|
|
}
|
|
}
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
}
|
|
|
|
def hscan(config, writable, readables, start, end, steps, passes=1, zigzag=False, before_stream=None, after_stream=None, after_read=None, title=None) {
|
|
/*
|
|
Hardware Scan: values sampled by external hardware and received asynchronously.
|
|
|
|
Args:
|
|
config(dict): Configuration of the hardware scan. The "class" key provides the implementation class.
|
|
Other keys are implementation specific.
|
|
writable(Writable): A positioner appropriated to the hardware scan type.
|
|
readables(list of Readable): Sensors appropriated to the hardware scan type.
|
|
start(float): start positions of writable.
|
|
end(float): final positions of writables.
|
|
steps(int or float): number of scan steps (int) or step size (float).
|
|
before_stream (function): callback before just before starting positioner move.
|
|
after_stream (function): callback before just after stopping positioner move.
|
|
after_read (function): callback on each readout.
|
|
Callback may have as optional parameters a ScanRecord object.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
|
|
*/
|
|
writable = string_to_obj(writable)
|
|
readables = to_array(string_to_obj(readables), "ch.psi.pshell.device.Readable")
|
|
start = to_array(start, 'd')
|
|
end = to_array(end, 'd')
|
|
steps = to_array(steps, steps[0].getClass()==java.lang.Integer ? 'i' : 'd')
|
|
scan = HardwareScan.newScan(config, writable,readables, start, end , steps, passes, zigzag);
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
}
|
|
|
|
def bscan(stream, records,Closure before_read=null, Closure after_read=null, title=null) {
|
|
/*
|
|
BS Scan: records all values in a beam synchronous stream.
|
|
|
|
Args:
|
|
stream(Stream): stream object
|
|
records(int): number of records to store
|
|
before_read (function): callback on each step, before each readout.
|
|
Callback may have as optional parameters list of positions.
|
|
after_read (function): callback on each step, after each readout.
|
|
Callback may have as optional parameters a ScanRecord object.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
|
|
*/
|
|
stream = string_to_obj(stream)
|
|
def scan = new BsScan(stream, (int)records) {
|
|
protected void onBeforeReadout(double[] pos) {
|
|
if (before_read!=null) before_read(pos)
|
|
}
|
|
protected void onAfterReadout(ScanRecord record) {
|
|
if (after_read!=null) after_read(record)
|
|
}
|
|
}
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
}
|
|
|
|
def tscan(readables, points, interval, Closure before_read=null, Closure after_read=null, title=null) {
|
|
/*
|
|
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.
|
|
before_read (function): callback on each step, before each readout.
|
|
after_read (function): callback on each step, after each readout.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
*/
|
|
if (interval>0){
|
|
interval= Math.max(interval, 0.001) //Minimum temporization is 1ms
|
|
}
|
|
def int latency_ms = interval * 1000
|
|
readables = to_array(string_to_obj(readables), "ch.psi.pshell.device.Readable")
|
|
def scan = new TimeScan(readables, points, latency_ms) {
|
|
protected void onBeforeReadout(double[] pos) {
|
|
if (before_read!=null) before_read(pos)
|
|
}
|
|
protected void onAfterReadout(ScanRecord record) {
|
|
if (after_read!=null) after_read(record)
|
|
}
|
|
}
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
|
|
}
|
|
|
|
def escan(name, title=null) {
|
|
/*
|
|
Epics Scan: execute an Epics Scan Record.
|
|
Args:
|
|
name(str): Name of scan record.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ScanResult object.
|
|
*/
|
|
def scan = new EpicsScan(name)
|
|
scan.setPlotTitle(title)
|
|
scan.start()
|
|
return scan.getResult()
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Data Plotting
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
def plot(data, name= null, xdata= null, ydata= null, title=null) {
|
|
/*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.
|
|
title(str, optional): plotting window name.
|
|
|
|
Returns:
|
|
ArrayList of Plot objects.
|
|
|
|
*/
|
|
|
|
if (name != null) {
|
|
if (get_length(name) == 0) {
|
|
name = null
|
|
} else {
|
|
if (data==null) {
|
|
data = []
|
|
for (def n in name) data.add([])
|
|
}
|
|
}
|
|
def plots = new PlotDescriptor[get_length(data)]
|
|
for (def i = 0; i < get_length(data); i++) {
|
|
def plotName = name ? name[i] : null
|
|
def x = xdata
|
|
if (get_length(x)>0) {
|
|
x = x[i]
|
|
}
|
|
def y = ydata
|
|
if (get_length(y)>0) {
|
|
y = y[i];
|
|
}
|
|
plots[i] = new PlotDescriptor(plotName, to_array(data[i], 'd'), to_array(x, 'd'), to_array(y, 'd'));
|
|
}
|
|
return get_context().plot(plots, title);
|
|
} else {
|
|
def plot = new PlotDescriptor(name, to_array(data, 'd'), to_array(xdata, 'd'), to_array(ydata, 'd'));
|
|
return get_context().plot(plot, title);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Data file access
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
def load_data(path, page=0) {
|
|
/*
|
|
Read data from the current persistence context or from data files.
|
|
|
|
Args:
|
|
path(str): Path to group or dataset relative to the persistence context root.
|
|
If in the format 'root|path' then read from path given by 'root'.
|
|
page(int, optional): Data page (used for 3D datasets)
|
|
Returns:
|
|
Data array
|
|
|
|
*/
|
|
def slice = get_context().dataManager.getData(path, page)
|
|
return slice.sliceData
|
|
}
|
|
|
|
def get_attributes(path) {
|
|
/*
|
|
Get the attributes from the current persistence context or from data files.
|
|
|
|
Args:
|
|
path(str): Path to group or dataset relative to the current persistence context root.
|
|
If in the format 'root|path' then read from path given by 'root'.
|
|
Returns:
|
|
Dictionary
|
|
|
|
*/
|
|
return get_context().dataManager.getAttributes(path)
|
|
}
|
|
|
|
def save_dataset(path, data) {
|
|
/*
|
|
Save data into a dataset within the current persistence context.
|
|
|
|
Args:
|
|
path(str): Path to dataset relative to the current persistence context root.
|
|
data (array or list): data to be saved
|
|
Returns:
|
|
Dictionary
|
|
|
|
*/
|
|
get_context().dataManager.setDataset(path, to_array(data, 'd'))
|
|
}
|
|
|
|
def create_dataset(path, type, unsigned=false, dimensions=null) {
|
|
/*
|
|
Create an empty dataset within the current persistence context.
|
|
|
|
Args:
|
|
path(str): Path to dataset relative to the current persistence context root.
|
|
type(str): array type 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float,
|
|
'd' = double, 'c' = char, 's' = String, 'o' = Object
|
|
unsigned(boolean, optional): create a dataset of unsigned type.
|
|
dimensions(tuple of int, optional): a 0 value means variable length in that dimension.
|
|
Returns:
|
|
null
|
|
|
|
*/
|
|
get_context().dataManager.createDataset(path, ScriptUtils.getType(type), unsigned, dimensions)
|
|
}
|
|
|
|
def create_table(path, names, types=null, lengths=null) {
|
|
/*
|
|
Create an empty table (dataset of compound type) within the current persistence context.
|
|
|
|
Args:
|
|
path(str): Path to dataset relative to the current persistence context root.
|
|
names(list of strings): name of each column
|
|
types(array of str): 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float,
|
|
'd' = double, 'c' = char, 's' = String, 'o' = Object
|
|
Note:A '[' prefix on type name indicates an array type.
|
|
lengths(list of int): the array length for each columns(0 for scalar types).
|
|
Returns:
|
|
null
|
|
|
|
*/
|
|
if (types != null) {
|
|
for (i = 0; i < types.length; i++)
|
|
types[i] = ScriptUtils.getType(types[i])
|
|
}
|
|
get_context().dataManager.createDataset(path, names, types, lengths)
|
|
}
|
|
|
|
def append_dataset(path, data, index=null) {
|
|
/*
|
|
Append data to dataset.
|
|
|
|
Args:
|
|
path(str): Path to dataset relative to the current persistence context root.
|
|
data(number or array or list): name of each column.
|
|
index(int, optional): if set then add the data in a specific position in the dataset.
|
|
Returns:
|
|
null
|
|
|
|
*/
|
|
data = to_array(data, 'd')
|
|
if (index == null)
|
|
get_context().dataManager.appendItem(path, data);
|
|
else
|
|
get_context().dataManager.setItem(path, data, index);
|
|
}
|
|
|
|
def append_table(path, data) {
|
|
/*
|
|
Append data to a table (dataset of compound type)
|
|
|
|
Args:
|
|
path(str): Path to dataset relative to the current persistence context root.
|
|
data(list): List of valus for each column of the table. Array types can be expressed as lists.
|
|
Returns:
|
|
null
|
|
|
|
*/
|
|
get_context().dataManager.appendItem(path, data)
|
|
}
|
|
|
|
def flush_data() {
|
|
/*
|
|
Flush all data files immediately.
|
|
Args:
|
|
null
|
|
Returns:
|
|
null
|
|
*/
|
|
get_context().dataManager.flush()
|
|
}
|
|
|
|
def set_attribute(path, name, value, unsigned=false) {
|
|
/*
|
|
Set an attribute to a group or dataset.
|
|
|
|
Args:
|
|
path(str): Path to dataset relative to the current persistence context root.
|
|
name(str): name of the atttribute
|
|
value(Object): the attribute value
|
|
unsigned(bool, optional): if applies, indicate if value is unsigned.
|
|
Returns:
|
|
null
|
|
*/
|
|
if (value.getClass()==java.math.BigDecimal){
|
|
value = value.toDouble();
|
|
}
|
|
get_context().dataManager.setAttribute(path, name, value, unsigned)
|
|
}
|
|
|
|
def log(log){
|
|
/*
|
|
Writes a log to the automatic data saving context - only if there is an ongoing scan or
|
|
script execution.
|
|
|
|
Args:
|
|
log(str): Log string.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().scriptingLog(String.valueOf(log))
|
|
get_context().dataManager.appendLog(String.valueOf(log))
|
|
}
|
|
|
|
def set_exec_pars(Map args){
|
|
/*
|
|
Configures the script execution parameters, overriding the system configuration.
|
|
|
|
Args:
|
|
args(dictionary). Keys:
|
|
name(str, optional): value of the {name} tag. Default is the running script name
|
|
(or "scan" in the case of a command line scan command.)
|
|
type(str, optional): value of the {type} tag. Default is empty.
|
|
This field can be used to store data in sub-folders of standard location.
|
|
path(str, optional): If defined provides the full path name for data output root (overriding config))
|
|
The tag {data} can be used to enter a path relative to the standard data folder.
|
|
layout(str, optional): Overrides default data layout.
|
|
depth_dim(int, optional): dimension of the depth for 2d-matrixes in 3d datasets.
|
|
persist(bool, optional): Overrides the configuration option to auto save scan data.
|
|
flush(bool, optional): Overrides the configuration option to flush file on each record.
|
|
accumulate(bool, optional): Overrides the configuration option to release scan records.
|
|
If false disable accumulation of scan records to scan result.
|
|
preserve(bool, optional): Overrides the configuration option to preserve device types.
|
|
If false all values are converted to double.
|
|
open(bool, optional): If true opens data output root (instead of only doing in the first data access call)
|
|
If false closes output root, if open.
|
|
reset(bool, optional): If true reset the scan counter - the {count} tag and set the timestamp to now.
|
|
group(str, optional): Overrides default layout group name for scans
|
|
tag(str, optional): Overrides default tag for scan names (affecting group or dataset name, according to layout)
|
|
defaults(bool, optional): If true restore the original execution parameters.
|
|
|
|
Graphical preferences can also be set. Keys are equal to lowercase of Preference enum:
|
|
"plot_disabled", "table_disabled", "enabled_plots", "plot_types", "print_scan", "auto_range",
|
|
"manual_range","domain_axis", "status".
|
|
See set_preference for more information.
|
|
|
|
Shortcut entries: "line_plots": list of devices with enforced line plots.
|
|
*/
|
|
get_context().setExecutionPars(args)
|
|
}
|
|
|
|
def get_exec_pars(){
|
|
/*
|
|
Returns script execution parameters.
|
|
|
|
Returns:
|
|
ExecutionContext object. Fields:
|
|
name (str): execution name - {name} tag.
|
|
type (str): execution type - {type} tag.
|
|
path (str): output data root.
|
|
open (bool): true if the output data root has been opened.
|
|
layout (str): data output layout. If None then using the configuration.
|
|
persist (bool): auto save scan data option.
|
|
flush (bool): flush file on each record.
|
|
index (int): current scan index.
|
|
group (str): data group currently used for scan data storage.
|
|
if no ongoing scan return "/" if within a script, or else None if a console command.
|
|
scanPath (str): dataset or group corresponding to current scan.
|
|
scan (Scan): reference to current scan, if any
|
|
source (CommandSource): return the source of the script or command.
|
|
args (obj): return the arguments for the script.
|
|
background (bool): return False if executing in main interpreter thread .
|
|
*/
|
|
return get_context().getExecutionPars()
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Epics Channels access
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def caget(name, type=null, size = null) {
|
|
/*
|
|
Reads an Epics PV.
|
|
|
|
Args:
|
|
name(str): PV name
|
|
type(str, optional): type of PV. By default gets the PV standard field type.
|
|
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, Epics.getChannelType(type), size)
|
|
}
|
|
|
|
def cawait(name, value, timeout=null, comparator = null, type=null, size = null) {
|
|
/*
|
|
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 or float, optional): if None waits for equality.
|
|
If a numeric value is provided, waits for channel to be in range.
|
|
type(str, optional): type of PV. By default gets the PV standard field type.
|
|
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:
|
|
None
|
|
*/
|
|
if (timeout!=null)
|
|
timeout = timeout * 1000
|
|
return Epics.waitValue(name, value, comparator, (int)timeout, Epics.getChannelType(type), size)
|
|
}
|
|
|
|
def caput(name, value, timeout=null) {
|
|
/*
|
|
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.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
if (timeout!=null)
|
|
timeout = timeout * 1000
|
|
return Epics.put(name, value, (int)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.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
return Epics.putq(name, value)
|
|
}
|
|
|
|
|
|
def create_channel(name, type = null, size = null) {
|
|
if (type == null) type = null;
|
|
return Epics.newChannel(name, Epics.getChannelType(type), size)
|
|
}
|
|
|
|
class Channel implements java.beans.PropertyChangeListener, Writable, Readable{
|
|
final channel
|
|
final callback
|
|
Channel(name, type = null, size = null, Closure callback=null){
|
|
/*
|
|
Create an object that encapsulates an Epics PV connection.
|
|
Args:
|
|
name(str): value to be written
|
|
type(str, optional): type of PV. By default gets the PV standard field type.
|
|
Scalar values: 'b', 'i', 'l', 'd', 's'.
|
|
Array: values: '[b', '[i,', '[l', '[d', '[s'.
|
|
size(int, optional): the size of the channel
|
|
callback(function, optional): The monitor callback.
|
|
*/
|
|
this.channel = Epics.newChannel(name, Epics.getChannelType(type), size)
|
|
this.callback = callback
|
|
}
|
|
|
|
def get_name(){
|
|
/*
|
|
Return the name of the channel.
|
|
*/
|
|
return channel.name
|
|
}
|
|
|
|
def get_size(){
|
|
/*
|
|
Return the size of the channel.
|
|
*/
|
|
return channel.size
|
|
}
|
|
|
|
def set_size(size){
|
|
/*
|
|
Set the size of the channel.
|
|
*/
|
|
channel.size = size
|
|
}
|
|
|
|
def is_connected(){
|
|
/*
|
|
Return True if channel is connected.
|
|
*/
|
|
return channel.connected
|
|
}
|
|
|
|
def is_monitored(){
|
|
/*
|
|
Return True if channel is monitored
|
|
*/
|
|
return channel.monitored
|
|
}
|
|
def set_monitored(boolean value){
|
|
/*
|
|
Set a channel monitor to trigger the callback function defined in the constructor.
|
|
*/
|
|
channel.monitored = value
|
|
if (value)
|
|
channel.addPropertyChangeListener(this);
|
|
else
|
|
channel.removePropertyChangeListener(this);
|
|
}
|
|
def void propertyChange(java.beans.PropertyChangeEvent pce){
|
|
if ((callback != null) && (pce.getPropertyName() == "value")){
|
|
callback(pce.getNewValue())
|
|
}
|
|
}
|
|
def put(value, timeout=None){
|
|
/*
|
|
Write to channel and wait value change. In the case of a timeout throws a TimeoutException.
|
|
Args:
|
|
value(obj): value to be written
|
|
timeout(float, optional): timeout in seconds. If none waits forever.
|
|
*/
|
|
if (timeout==None)
|
|
channel.setValue(value);
|
|
else
|
|
channel.setValueAsync(value).get((int)(timeout*1000), java.util.concurrent.TimeUnit.MILLISECONDS);
|
|
}
|
|
|
|
def putq(value){
|
|
/*
|
|
Write to channel and don't wait.
|
|
*/
|
|
channel.setValueNoWait(value)
|
|
}
|
|
|
|
def get(){
|
|
/*
|
|
Get channel value.
|
|
*/
|
|
return channel.getValue()
|
|
}
|
|
|
|
def wait_for_value(value, timeout=None, comparator=None){
|
|
/*
|
|
Wait channel to reach a value, using a given comparator. In the case of a timeout throws a TimeoutException.
|
|
Args:
|
|
value(obj): value to be verified.
|
|
timeout(float, optional): timeout in seconds. If None waits forever.
|
|
comparator (java.util.Comparator, optional). If None, uses Object.equals.
|
|
*/
|
|
if (comparator == null){
|
|
if (timeout == null)
|
|
channel.waitForValue(value);
|
|
else
|
|
channel.waitForValue(value, (int)(timeout*1000));
|
|
} else{
|
|
if (timeout == null)
|
|
channel.waitForValue(value, comparator);
|
|
else
|
|
channel.waitForValue(value, comparator, (int)(timeout*1000));
|
|
}
|
|
}
|
|
|
|
def close(){
|
|
/*
|
|
Close the channel.
|
|
*/
|
|
channel.destroy()
|
|
}
|
|
//Writable interface
|
|
def void write(value){
|
|
putq(value)
|
|
}
|
|
//Readable interface
|
|
def read() {
|
|
return get()
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Concurrent execution
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
def fork(Closure[] closures){
|
|
"""Start execution of functions in parallel.
|
|
|
|
Args:
|
|
*functions(function references)
|
|
|
|
Returns:
|
|
List of callable objects
|
|
"""
|
|
//callables = []
|
|
//for m in functions:
|
|
// if is_list(m):
|
|
// callables.append(Callable(m[0],*m[1]))
|
|
// else:
|
|
// callables.append(Callable(m))
|
|
return Threading.fork(closures)
|
|
}
|
|
|
|
def join(futures){
|
|
"""Wait parallel execution of functions.
|
|
|
|
Args:
|
|
futures(list of Future) : as returned from fork
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
try{
|
|
return Threading.join(futures)
|
|
} catch (java.util.concurrent.ExecutionException ex){
|
|
throw ex.getCause()
|
|
}
|
|
}
|
|
|
|
def parallelize(Closure[] closures){
|
|
"""Equivalent to fork + join
|
|
|
|
Args:
|
|
*functions(function references)
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
futures = fork(closures)
|
|
return join(futures)
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Script evaluation and Background task control.
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
Object run(script_name, args = null, locals = null) {
|
|
/*
|
|
Run script: can be absolute path, relative, or short name to be search in the path.
|
|
Args:
|
|
args(Dict ot List): Sets Sys.argv (if list) or gobal variables(if dict) to the script.
|
|
locals(Dict): If not null sets the locals()for the runing script.
|
|
If locals is used then script definitions will not go to global namespace.
|
|
|
|
Returns:
|
|
The script return value (if set with set_return)
|
|
*/
|
|
get_context().scriptManager.evalFile(script_name)
|
|
}
|
|
|
|
def abort() {
|
|
/*
|
|
Abort the execution of ongoing task. It can be called from the script to quit.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
//Cannot be on script execution thread
|
|
def task = {
|
|
get_context().abort()
|
|
}
|
|
|
|
fork(task)
|
|
return null
|
|
}
|
|
|
|
def start_task(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.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
def delay_ms = delay * 1000
|
|
def interval_ms = (interval>=0) ? interval * 1000 : interval
|
|
get_context().taskManager.create(script, (int)delay_ms, (int)interval_ms)
|
|
get_context().taskManager.start(script)
|
|
}
|
|
|
|
def stop_task(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
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().taskManager.remove(script, force)
|
|
}
|
|
|
|
def set_return(value){
|
|
/*
|
|
Sets the script return value. This value is returned by the "run" function.
|
|
|
|
Args:
|
|
value(Object): script return value.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
if (is_interpreter_thread()){
|
|
_=value
|
|
}
|
|
__THREAD_EXEC_RESULT__[Thread.currentThread()]=value //Used when running file
|
|
return value //Used when parsing file
|
|
}
|
|
|
|
|
|
def is_interpreter_thread(){
|
|
return Thread.currentThread().name == "Interpreter Thread"
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//UI interaction
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def set_status(status){
|
|
/*
|
|
Set the application status.
|
|
|
|
Args:
|
|
status(str): new status.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
set_preference(Preference.STATUS, status)
|
|
}
|
|
|
|
def set_preference(preference, value){
|
|
/*
|
|
Hints to graphical layer:
|
|
|
|
Args:
|
|
preference(Preference): Preference name
|
|
Preference.SCAN_PLOT_DISABLED #enable/disable scan plot (True/False)
|
|
Preference.SCAN_TABLE_DISABLED #enable/disable scan table (True/False)
|
|
Preference.ENABLED_PLOTS #select Readables to be plotted (list of Readable or
|
|
String (Readable names))
|
|
Preference.PLOT_TYPES #Dictionary or (Readable or String):(String or int) pairs
|
|
where the key is a plot name and the value is the desired plot type
|
|
Preference.PRINT_SCAN #Print scan records to console
|
|
Preference.AUTO_RANGE # Automatic range scan plots x-axis
|
|
Preference.MANUAL_RANGE # Manually set scan plots x-axis
|
|
Preference.DOMAIN_AXIS #Set the domain axis source: "Time", "Index", or a readable name.
|
|
Default(None): first positioner
|
|
Preference.STATUS # set application status
|
|
value(object): preference value
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
if (get_rank(value)>0){
|
|
value = to_array(value, 'o')
|
|
}
|
|
get_context().setPreference(preference, value)
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Versioning tools
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
def commit(message){
|
|
/*
|
|
Commit the changes to the repository. If manual commit is not configured then there is no need to call this function: commits are made as needed.
|
|
|
|
Args:
|
|
message(str): commit message
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().commit(message)
|
|
}
|
|
|
|
def add_repository(path){
|
|
/*
|
|
Add file or folder to repository
|
|
|
|
Args:
|
|
path(str): relative path to the home folder.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().addRepository(path)
|
|
}
|
|
|
|
def diff(){
|
|
/*
|
|
Return list of changes in the repository
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
return get_context().diff()
|
|
}
|
|
|
|
|
|
def checkout_tag(tag){
|
|
/*
|
|
Checkout a tag name.
|
|
|
|
Args:
|
|
tag(str): tag name.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().checkoutTag(tag)
|
|
}
|
|
|
|
def checkout_branch(tag){
|
|
/*
|
|
Checkout a local branch name.
|
|
|
|
Args:
|
|
tag(str): branch name.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().checkoutLocalBranch(tag)
|
|
}
|
|
|
|
def pull_repository(){
|
|
/*
|
|
Push to remote repository.
|
|
|
|
Args:
|
|
all_branches(boolean, optional): all branches or just current.
|
|
force(boolean, optional): force flag.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().pullFromUpstream()
|
|
}
|
|
|
|
def push_repository(all_branches=true, force=false){
|
|
/*
|
|
Push to remote repository.
|
|
|
|
Args:
|
|
all_branches(boolean, optional): all branches or just current.
|
|
force(boolean, optional): force flag.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().pushToUpstream(all_branches, force)
|
|
}
|
|
|
|
def cleanup_repository(){
|
|
/*
|
|
Performs a repository cleanup.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().cleanupRepository()
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Device Pool & Imaging setup
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
def get_device(device_name){
|
|
/*
|
|
Returns a configured device (or imaging source) by its name.
|
|
|
|
Args:
|
|
device_name(str): name of the device.
|
|
|
|
Returns:
|
|
device
|
|
*/
|
|
return get_context().devicePool.getByName(device_name)
|
|
}
|
|
|
|
def add_device(device, force = false){
|
|
/*
|
|
Add a device (or imaging source) to the device pool.
|
|
|
|
Args:
|
|
device(Device or Source): device object.
|
|
force(boolean, optional): if true then dispose existing device with same name.
|
|
Otherwise will fail in case of name clash.
|
|
|
|
Returns:
|
|
True if device was added, false if was already in the pool, or exception in case of name clash.
|
|
*/
|
|
if (get_context().devicePool.contains(device)){
|
|
return false
|
|
}
|
|
if (force){
|
|
dev = get_context().devicePool.getByName(device.getName())
|
|
if (dev != null)
|
|
remove_device(dev)
|
|
}
|
|
return get_context().devicePool.addDevice(device)
|
|
}
|
|
|
|
def remove_device(device){
|
|
/*
|
|
Remove a device (or imaging source) from the device pool.
|
|
|
|
Args:
|
|
device(Device or Source): device object.
|
|
|
|
Returns:
|
|
bool: true if device was removed.
|
|
|
|
*/
|
|
return get_context().devicePool.removeDevice(device)
|
|
}
|
|
|
|
|
|
def set_device_alias(device, alias){
|
|
/*
|
|
Set a device alias to be used in scans (datasets and plots).
|
|
|
|
Args:
|
|
device(Device): device object.
|
|
alias(str): replace device name in scans.
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().dataManager.setAlias(device, alias)
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Utilities
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
def string_to_obj(obj) {
|
|
if (obj instanceof String) {
|
|
return evaluate(obj)
|
|
} else if (obj instanceof Array) {
|
|
ret = []
|
|
for (i = 0; i < o.length; i++)
|
|
ret[i] = string_to_obj(obj[i]);
|
|
return ret
|
|
}
|
|
return obj
|
|
}
|
|
|
|
def stop(){
|
|
/*
|
|
Stop all devices implementing the Stoppable interface.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().stopAll()
|
|
}
|
|
|
|
def update(){
|
|
/*
|
|
Update all devices.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
*/
|
|
get_context().updateAll()
|
|
}
|
|
|
|
def inject(){
|
|
/*
|
|
Restore initial globals: re-inject devices and startup variables to the interpreter.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
|
|
*/
|
|
get_context().injectVars()
|
|
}
|
|
|
|
|
|
def sleep(seconds){
|
|
Thread.sleep(millis * 1000);
|
|
}
|
|
|
|
_ = true |