diff --git a/config/config.properties b/config/config.properties index 54499aa7..ca88f2cd 100644 --- a/config/config.properties +++ b/config/config.properties @@ -1,4 +1,4 @@ -#Fri Aug 14 14:26:45 CEST 2015 +#Tue Sep 01 09:59:03 CEST 2015 autoSaveScanData=true createSessionFiles=false dataFile={data}/{year}_{month}/{date}/{date}_{time}_{context} @@ -25,4 +25,5 @@ userAuthenticator= userManagement=false versionTrackingEnabled=true versionTrackingLogin={context}/agkey +versionTrackingManual=false versionTrackingRemote=ssh\://git@github.psi.ch\:7999/psd/x03da.git diff --git a/plugins/HoloScan.java b/plugins/HoloScan.java index 7ca7aab6..20ae6587 100644 --- a/plugins/HoloScan.java +++ b/plugins/HoloScan.java @@ -2,7 +2,7 @@ * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved. */ -import ch.psi.pshell.dev.Motor; +import ch.psi.pshell.device.Motor; import ch.psi.pshell.epics.Scienta; import ch.psi.pshell.ui.Panel; import ch.psi.utils.State; diff --git a/plugins/ManipulatorScan.java b/plugins/ManipulatorScan.java index 15f6f8eb..b5c9b151 100644 --- a/plugins/ManipulatorScan.java +++ b/plugins/ManipulatorScan.java @@ -2,7 +2,7 @@ * Copyright (c) 2014 Paul Scherrer Institute. All rights reserved. */ -import ch.psi.pshell.dev.Motor; +import ch.psi.pshell.device.Motor; import ch.psi.pshell.epics.Scienta; import ch.psi.pshell.ui.Panel; import ch.psi.utils.State; diff --git a/plugins/XPSSpectrum.java b/plugins/XPSSpectrum.java index 63bc0a23..f5b9fda9 100644 --- a/plugins/XPSSpectrum.java +++ b/plugins/XPSSpectrum.java @@ -3,8 +3,9 @@ */ import ch.psi.pshell.core.Controller; -import ch.psi.pshell.dev.Device; -import ch.psi.pshell.dev.DeviceListener; +import ch.psi.pshell.device.Device; +import ch.psi.pshell.device.DeviceAdapter; +import ch.psi.pshell.device.DeviceListener; import ch.psi.pshell.epics.Scienta; import ch.psi.pshell.plot.LinePlotSeries; import ch.psi.pshell.plot.RangeSelectionPlot.RangeSelection; @@ -136,11 +137,7 @@ public class XPSSpectrum extends Panel { btSave.setEnabled(buttonDetailedScan.isEnabled()); } - DeviceListener progressListener = new DeviceListener() { - - @Override - public void onStateChanged(Device device, State state, State state1) { - } + DeviceListener progressListener = new DeviceAdapter() { @Override public void onValueChanged(Device device, Object o, Object o1) { diff --git a/script/local.py b/script/local.py index ffb3b5c8..665ed03e 100644 --- a/script/local.py +++ b/script/local.py @@ -1,5 +1,5 @@ import random -import ch.psi.pshell.dev.Readable.ReadableArray as ReadableArray +import ch.psi.pshell.device.Readable.ReadableArray as ReadableArray class SimulatedOutput(Writable): def write(self, value): diff --git a/script/tutorial/01_LineScan.py b/script/tutorial/01_LineScan.py new file mode 100644 index 00000000..28089901 --- /dev/null +++ b/script/tutorial/01_LineScan.py @@ -0,0 +1,25 @@ +""" +Demonstrate the use of Line Scan: one or multiple positioners move together linearly. +""" + +#This optional preference limits the displayed plots +#set_preference(Preference.ENABLED_PLOTS, [ai1, ai2,]) + +#This optional preference displays wf1 as a 1d plot at each scan point, instead of a matrix plot +#set_preference(Preference.PLOT_TYPES, {wf1:1}) + +#Execute the scan: 200 steps, a1 from 0 to 40 +a= lscan(ao1, (ai1,ai2,wf1,im1), 0, 40, 200, 0.01) + +#Alternative: Steps of size 0.1, a1 from 0 to 40 +#a= lscan(ao1, (ai1,ai2,wf1,im1), 0, 40, 0.5, 0.01) + +#2 positioners moving together in 200 steps, a1 from 0 to 40 and a2 from 0 to 100 +#a= lscan((ao1,ao2), (ai1,ai2,wf1,im1), (0, 0), (40, 100), 200, 0.01) + +#Setting attributes to the scan group +path = get_current_group() +set_attribute(path, "AttrString", "Value") +set_attribute(path, "AttrInteger", 1) +set_attribute(path, "AttrDouble", 2.0) +set_attribute(path, "AttrBoolean", True) diff --git a/script/tutorial/02_ScanCallbacks.py b/script/tutorial/02_ScanCallbacks.py new file mode 100644 index 00000000..cee650ce --- /dev/null +++ b/script/tutorial/02_ScanCallbacks.py @@ -0,0 +1,20 @@ +""" +Demonstrate use of scan callbacks to trigger a detector at falling edge. +""" + +def BeforeReadout(): + ao1.write(1) + ao1.write(0) + + #Example with an epics direct channel access + #caput("CHANNEL_NAME", 1) + #caput("CHANNEL_NAME", 0) + +index=0 + +def AfterReadout(): + global index + print "Aquired frame: " + str(index) + index=index+1 + +a= lscan((m1,m2), (ai1, ai2), (0,0), (4,8), steps=20, latency = 0.01, before_read=BeforeReadout, after_read=AfterReadout) \ No newline at end of file diff --git a/script/tutorial/03_ManipulatingScanData.py b/script/tutorial/03_ManipulatingScanData.py new file mode 100644 index 00000000..adc958db --- /dev/null +++ b/script/tutorial/03_ManipulatingScanData.py @@ -0,0 +1,21 @@ +""" +Processing and plotting scan data. +""" + +ao1.write(0.0) +scan1= lscan(ao1, (ai1,ai2,wf1), 0, 40, 40, 0.01, False, "Scan 1") +scan2= lscan(ao1, (ai1,ai2,wf1), 0, 40, 40, 0.01, False, "Scan 2") + + +from operator import add +result = map(add, scan1.getReadable(0), scan2.getReadable(0)) + +#Alternative: +#result=[] +#for i in range(len(scan1.records)): +# result.append(scan1.records[i].values[0]+scan2.records[i].values[0]) + + +plot(result) +print result + diff --git a/script/tutorial/04_AreaScan.py b/script/tutorial/04_AreaScan.py new file mode 100644 index 00000000..9f594e38 --- /dev/null +++ b/script/tutorial/04_AreaScan.py @@ -0,0 +1,12 @@ +""" +Area Scan: Multiple positioners, each one is one dimension. +""" + +#This optional preference displays wf1 as a 1d plot at each scan point, instead of a matrix plot +#set_preference(Preference.PLOT_TYPES, {wf1:1}) + +#The second sensor is an array. In the plot window it is overwritten in every same x position. +#The data window never displays 3d data, but the 3d data can be accesses during the scan in the Data tab. +ascan((m1,m2), (ai1,wf1), (0.0,0.0), (2.0,1.0), (20,20)) + + diff --git a/script/tutorial/05_RelativeScan.py b/script/tutorial/05_RelativeScan.py new file mode 100644 index 00000000..8809d6a1 --- /dev/null +++ b/script/tutorial/05_RelativeScan.py @@ -0,0 +1,12 @@ +""" +Demonstrate use of Relative Line Scan. +The arguments start and end are relative to the current position. +After the scan the positioner(s) move back to the initial position. +""" + +print "Initial position = " + str(m1.position) + +a= lscan(m1, (ai1,ai2,wf1,im1), start = -2, end =2, steps = 20, relative = True) + + +print "Final position = " + str(m1.position) \ No newline at end of file diff --git a/script/tutorial/06_VectorScan.py b/script/tutorial/06_VectorScan.py new file mode 100644 index 00000000..a845c2df --- /dev/null +++ b/script/tutorial/06_VectorScan.py @@ -0,0 +1,22 @@ +""" +Demonstrate use of Vector Scan: one or multiple positioners set according to a position vector. +""" + +#1D vector scan, plot to 1D Vector tab +vector = [ 1, 3, 5, 10, 25, 40, 45, 47, 49] +a= vscan(ao1,(ai1,ai2),vector,False, 0.5, context = "1D Vector") + + + + +#2D vector scan, plot to 2D Vector tab +vector = [ [1,1] , [1,2] , [1,3] , [1,4] , + [1.5,2.5] , + [2,1] , [2,2] , [2,3] , [2,4] , + [2.5,2.5] , + [3,1] , [3,2] , [3,3] , [3,4] ] + +a= vscan((m1,m2),(ai1,ai2),vector,False, 0.1, context = "2D Vector") + + + diff --git a/script/tutorial/07_MathFit.py b/script/tutorial/07_MathFit.py new file mode 100644 index 00000000..d9d8dc87 --- /dev/null +++ b/script/tutorial/07_MathFit.py @@ -0,0 +1,58 @@ +""" +Function fitting and peak search with mathutils.py +""" + +from mathutils import fit_polynomial,fit_gaussian, fit_harmonic, calculate_peaks +from mathutils import PolynomialFunction, Gaussian, HarmonicOscillator +import math + + +start = 0 +end = 10 +step_size = 0.1 + +result= lscan(ao1,ai1,start,end,[step_size,],0.01) + +readable = result.getReadable(0) +positions = result.getPositions(0) + +def get_function_data(function, start, end, resolution): + ret = [] + for x in frange(start, end, resolution, True): + fit_polinomial.append(function.value(x)) + + +pars_polynomial = (a0, a1, a2, a3, a4, a5, a6) = fit_polynomial(readable, positions, 6) +fitted_polynomial_function = PolynomialFunction(pars_polynomial) +print pars_polynomial + +(normalization, mean, sigma) = fit_gaussian(readable, positions, True) +fitted_gaussian_function = Gaussian(normalization, mean, sigma) +print (normalization, mean, sigma) + +(amplitude, angular_frequency, phase) = fit_harmonic(readable, positions) +fitted_harmonic_function = HarmonicOscillator(amplitude, angular_frequency, phase) +print (amplitude, angular_frequency, phase) + + +resolution = step_size/100 +fit_polinomial = [] +fit_gaussian = [] +fit_harmonic = [] +for x in frange(start,end,resolution, True): + fit_polinomial.append(fitted_polynomial_function.value(x)) + fit_gaussian.append(fitted_gaussian_function.value(x)) + fit_harmonic.append(fitted_harmonic_function.value(x)) +x = frange(start, end+resolution, resolution) + + +peaks = calculate_peaks(fitted_polynomial_function) + +plots = plot([readable, fit_polinomial, fit_gaussian, fit_harmonic] , + ["data", "polinomial", "gaussian", "harmonic"], xdata = [positions,x,x,x], context="Data") + +for p in peaks: + print "Max: " + str(p) + plots[0].addMarker(p, None, "Max=" + str(round(p,2)), None) +import java.awt.Color +plots[0].addMarker(mean, None, "Mean=" + str(round(mean,2)), java.awt.Color.LIGHT_GRAY) \ No newline at end of file diff --git a/script/tutorial/08_MathMultipleGaussians.py b/script/tutorial/08_MathMultipleGaussians.py new file mode 100644 index 00000000..f9165d0a --- /dev/null +++ b/script/tutorial/08_MathMultipleGaussians.py @@ -0,0 +1,37 @@ +""" +Multiple Gaussians peak search +""" + +from mathutils import estimate_peak_indexes, fit_gaussians, create_fit_point_list + +start = 0 +end = 50 +step_size = 0.2 + +result= lscan(ao1,ai1,start,end,[step_size,]) + +readable = result.getReadable(0) +positions = result.getPositions(0) + +threshold = (min(readable) + max(readable))/2 +min_peak_distance = 5.0 + +peaks = estimate_peak_indexes(readable, positions, threshold, min_peak_distance) +print "Peak indexes: " + str(peaks) +print "Peak x: " + str(map(lambda x:positions[x], peaks)) +print "Peak y: " + str(map(lambda x:readable[x], peaks)) + + + +gaussians = fit_gaussians(readable, positions, peaks) + + +plots = plot([readable],["sin"],[positions], context="Data" ) +for i in range(len(peaks)): + peak = peaks[i] + (norm, mean, sigma) = gaussians[i] + if abs(mean - positions[peak]) < min_peak_distance: + print "Peak -> " + str(mean) + plots[0].addMarker(mean, None, "N="+str(round(norm,2)), None) + else: + print "Invalid gaussian fit: " + str(mean) diff --git a/script/tutorial/09_PseudoDevices.py b/script/tutorial/09_PseudoDevices.py new file mode 100644 index 00000000..9f655d06 --- /dev/null +++ b/script/tutorial/09_PseudoDevices.py @@ -0,0 +1,26 @@ +""" +Using pseudo-device to : + - Add calculations to scan data. + - Execute logic during scan +""" + +class Clock(Readable): + def read(self): + return time.clock() + +class Averager(Readable): + def read(self): + arr = wf1.take() #Gets the CACHED waveform + return reduce(lambda x, y: x + y, arr) / len(arr) + +class Positioner(Writable): + def write(self,pos): + print "Step = " + str(pos) + +averager=Averager() +clock=Clock() +positioner=Positioner() + +a= lscan((ao1,positioner),(ai2,wf1,averager,clock),(0,0),(40,20),20,0.1) + + diff --git a/script/tutorial/10_DeviceListener.py b/script/tutorial/10_DeviceListener.py new file mode 100644 index 00000000..7cec0cc7 --- /dev/null +++ b/script/tutorial/10_DeviceListener.py @@ -0,0 +1,25 @@ +""" +Create a device listener to interrupt the scan +""" +import java.lang.InterruptedException + +class Listener (DeviceListener): + def onStateChanged(self, device, state, former): + pass + def onValueChanged(self, device, value, former): + if value > 1.01: + print "Value over limit-> aborting" + abort() + +listener = Listener() + +ai1.addListener(listener) +try: + lscan(ao1, (ai1), 0, 40, 200, 0.01) +except java.lang.InterruptedException: + print "Aborted" +finally: + ai1.removeListener(listener) + + + diff --git a/script/tutorial/11_ParametersAndReturn.py b/script/tutorial/11_ParametersAndReturn.py new file mode 100644 index 00000000..e5dda018 --- /dev/null +++ b/script/tutorial/11_ParametersAndReturn.py @@ -0,0 +1,22 @@ +""" +Settign script parameters and return value +""" + + +#Providing an array of global variables +#run ("tutorial/11_ParametersAndReturn", {"start":10.0, "end":50.0, "step":40}) + +#Providing the locals dictionary +# The parameters are not set as globals, and nor script definitions +#run ("tutorial/11_ParametersAndReturn", locals={"start":10.0, "end":50.0, "step":40}) + +#Setting sys.argv: +#run ("tutorial/11_ParametersAndReturn", [10.0, 50.0, 40]) +#start = sys.argv[0] +#end = sys.argv[1] +#step = sys.argv[2] + + +a= lscan(ao1, ai1, start, end, step, 0.1) +a.getReadable(0) +set_return(a.getReadable(0)) \ No newline at end of file diff --git a/script/tutorial/12_ManualScan.py b/script/tutorial/12_ManualScan.py new file mode 100644 index 00000000..50d9a05f --- /dev/null +++ b/script/tutorial/12_ManualScan.py @@ -0,0 +1,33 @@ +""" +Manual scan: Manually setting positioners and reading back sensors, but still using +the standard data handling and plotting of built-in scans. +""" + +MOTOR_RANGE = (0.0, 8.0) +OUTPUT_SETPOINTS = (1.0, 2.0, 3.0) +FIXED_X = True + + +writables_names = [m1.getName(), ao1.getName()] +readable_names = [ai1.getName(), ai2.getName()] +start = [ MOTOR_RANGE[0] if FIXED_X else -1, OUTPUT_SETPOINTS[0]] +stop = [ MOTOR_RANGE[1] if FIXED_X else -1, OUTPUT_SETPOINTS[-1]] +steps = [int(MOTOR_RANGE[1]-MOTOR_RANGE[0]), len(OUTPUT_SETPOINTS)-1 if FIXED_X else -1] + +scan = ManualScan(writables_names, readable_names ,start, stop, steps) + + +#This option is to plot the foe each output value one 1D series, intead of all in a matrix plot +set_preference(Preference.PLOT_TYPES, {ai1:1,ai2:1}) + + +scan.start() +m1.setSpeed(10.0) +for setpoint1 in frange(MOTOR_RANGE[0], MOTOR_RANGE[1], 1.0, True): + m1.move(setpoint1) + for setpoint2 in OUTPUT_SETPOINTS: + ao1.write(setpoint2) + scan.append ([setpoint1, setpoint2], [m1.read(), ao1.read()], [ai1.read(), ai2.read()]) + + +scan.end() diff --git a/script/tutorial/13_CustomPlot.py b/script/tutorial/13_CustomPlot.py new file mode 100644 index 00000000..3d9d9700 --- /dev/null +++ b/script/tutorial/13_CustomPlot.py @@ -0,0 +1,19 @@ +""" +Custom plot: Example of creating a 1D plot for a 2D scan where each scanned row is a series + +""" + +#Setting the 1d preference would create in the place of the matrix plot, a 1d plot where +#each scanned column is a series +#set_preference(Preference.PLOT_TYPES, {'ai1':1}) + + + +p = plot(None, context="1d Plot")[0] +def AfterReadout(record, scan): + if record.setpoints[1] == scan.getStart()[1]: + p.addSeries(LinePlotSeries(str(record.positions[0]))) + p.getSeries(p.numberOfSeries-1).appendData(record.positions[1], record.values[0]) + + +ascan((ao1,ao2), (ai1), (0,10), (20,30), (20,20), 0.1, after_read=AfterReadout) \ No newline at end of file diff --git a/script/tutorial/14_DataManipulation.py b/script/tutorial/14_DataManipulation.py new file mode 100644 index 00000000..fd47f309 --- /dev/null +++ b/script/tutorial/14_DataManipulation.py @@ -0,0 +1,68 @@ +""" +Data Manipulation: Using the data access API to generate and retrieve data + +""" + +#Creating a 1D dataset from an array +path="group/data1" +data1d = [1.0, 2.0, 3.0, 4.0, 5.0] +save_dataset(path, data1d) +#Reading ii back +read =load_data(path) +print read.tolist() +assert data1d==read.tolist() +plot(read) + +#Creating a 2D dataset from an array with some attributes +data2d = [ [1.0, 2.0, 3.0, 4.0, 5.0], [2.0, 3.0, 4.0, 5.0, 6.0, ], [3.0, 4.0, 5.0, 6.0, 7.0]] +path="group/data2" +save_dataset(path, data2d) +set_attribute(path, "AttrString", "Value") +set_attribute(path, "AttrInteger", 1) +set_attribute(path, "AttrDouble", 2.0) +set_attribute(path, "AttrBoolean", True) +#Reading it back +read =load_data(path) +print read.tolist() +plot(read) + +#Creating a 3D dataset from an array +data3d = [ [ [1,2,3,4,5], [2,3,4,5,6], [3,4,5,6,7]], [ [3,2,3,4,5], [4,3,4,5,6], [5,4,5,6,7]]] +path="group/data3" +save_dataset(path, data3d) +#Reading it back +read =load_data(path,0) +print read.tolist() +read =load_data(path,1) +print read.tolist() + +#Creating a INT dataset adding elements one by one +path = "group/data4" +create_dataset(path, 'i') +for i in range(10): + append_dataset(path,i) + + +#Creating a 2D data FLOAT dataset adding lines one by one +path = "group/data5" +create_dataset(path, 'd', False, (0,0)) +for row in data2d: + append_dataset(path, row) + + +#Creating a Table (compund type) +path = "group/data6" +names = ["a", "b", "c", "d"] +types = ["d", "d", "d", "[d"] +lenghts = [0,0,0,5] +table = [ [1,2,3,[0,1,2,3,4]], + [2,3,4,[3,4,5,6,7]], + [3,4,5,[6,7,8,9,4]] ] +create_table(path, names, types, lenghts) +for row in table: + append_table(path, row) +flush_data() +#Read it back +read =load_data(path) +print read + diff --git a/script/tutorial/15_Pararellization.py b/script/tutorial/15_Pararellization.py new file mode 100644 index 00000000..e2d35f51 --- /dev/null +++ b/script/tutorial/15_Pararellization.py @@ -0,0 +1,51 @@ +""" +Using Pararellization API to execute tasks concurrently + +""" +import traceback + +#Simple parallization +def task1(): + m1.moveRel(1.0) + return m1.getPosition() + +def task2(): + m2.moveRel(1.0) + return m1.getPosition() + +def task3(): + return ai1.read() + +ret = parallelize(task1, task2, task3) +print ret + + +#Fork amd join +ret = fork(task1, task2, task3) +print ai1.read() +ret = join(ret) +print ret + + + +#Functions with parameters +def moveRelative(motor, step): + print "Moving " + motor.getName() + " step = " + str(step) + motor.moveRel(step) + return motor.getPosition() + +ret = parallelize((moveRelative,(m1,-2)), (moveRelative,(m2,-2))) +print ret + + +#Exception in parallel task is thrown back to script +try: + parallelize((moveRelative,(ai1,1)), (moveRelative,(ai2,1))) +except: + print "Ok, caught exception:" + traceback.print_exc() + + + + + diff --git a/script/tutorial/16_SimultaneousScans.py b/script/tutorial/16_SimultaneousScans.py new file mode 100644 index 00000000..4fe0c11e --- /dev/null +++ b/script/tutorial/16_SimultaneousScans.py @@ -0,0 +1,15 @@ +""" +Example on running simultaneous scans. They should not manipulate same writables +""" + + +def scan1(): + print "scan1" + return lscan(ao1, ai1, 0, 40, 20, 0.1, context = "scan1") + +def scan2(): + print "scan2" + return lscan(ao2, wf1, 0, 40, 20, 0.1, context = "scan2") + + +parallelize(scan1, scan2) diff --git a/script/tutorial/17_DirectEpicsAccess.py b/script/tutorial/17_DirectEpicsAccess.py new file mode 100644 index 00000000..ebf30806 --- /dev/null +++ b/script/tutorial/17_DirectEpicsAccess.py @@ -0,0 +1,31 @@ +""" +EPICS direct channel access. +EPICS devices implemented are included in PShell, package ch.psi.pshell.epics. +However direct channel access builtin functions are available. +""" + +channel_name = "TESTIOC:TESTCALCOUT:Output" + +#reading/writing to a channel +print (caget(channel_name)) +caput(channel_name, 0.0) +#Put with no wait +caput(channel_name, 0.0) +print (caget(channel_name)) + +#waiting for a channel valur +cawait(channel_name, 0.0, timeout = 10.0) + +#If many IO it is better to keep the same CA connection +channel = Channel(channel_name, 'd') +for i in range(100): + print channel.get() +lscan(channel, (ai2,channel), 0, 10, 0.1) + +#The channel class implements Readable and Writable and therefore can be used in scans +lscan(channel, ai2, 0, 10, 0.1) + +#Or else we can use a Device +import ch.psi.pshell.epics.ChannelDouble as ChannelDouble +channel = ChannelDouble("My Channel", channel_name) +channel.initialize() \ No newline at end of file diff --git a/script/tutorial/18_Imaging.py b/script/tutorial/18_Imaging.py new file mode 100644 index 00000000..56c9b369 --- /dev/null +++ b/script/tutorial/18_Imaging.py @@ -0,0 +1,27 @@ +""" +Demonstrate creation of an image filter, applying it to a source and creating a new source +based on the filter. +""" + + +import ch.psi.pshell.imaging.Filter as Filter +from ch.psi.pshell.imaging.Utils import * + + +class MyFilter(Filter): + def process(self, image, data): + image = grayscale(image) + image = blur(image) + image = sobel(image) + return image + +#Setting the filter to a source +src1.setFilter(MyFilter()) + +#Creating a new source with the filter +src2.setFilter(None) +add_device(MyFilter("f1"), True) +#f1.passive = True +src2.addListener(f1) + + diff --git a/script/tutorial/19_ContinuousScan.py b/script/tutorial/19_ContinuousScan.py new file mode 100644 index 00000000..0aed6404 --- /dev/null +++ b/script/tutorial/19_ContinuousScan.py @@ -0,0 +1,15 @@ +""" +Demonstrate the use of Continuous Scan Scan: a Linear Scan with continuous motor move and +sampling on the fly. +""" + +#A single motor at current speed +a= cscan(m1, (ai1,ai2), -2, 3 , steps=100, relative=True) + + +#A single motor in a given time +a= cscan(m1, (ai1,ai2), -2.0, 3.0, steps=100 ,time = 4.0, relative=True) + +#Multiple motors in a given time +a= cscan((m1, m2), (ai1,ai2), (-2.0, -3), (3.0, 5.0), steps=100,time = 4.0, relative=True) + diff --git a/script/tutorial/20_Interlocks.py b/script/tutorial/20_Interlocks.py new file mode 100644 index 00000000..c562ea65 --- /dev/null +++ b/script/tutorial/20_Interlocks.py @@ -0,0 +1,46 @@ +""" +Interlocks: example on creating and installing device interlock rules. + +""" + + +class MyInterlock1 (Interlock): +#Motor and Positioners + def __init__(self): + Interlock.__init__(self, (m1, p1)) + + def check(self, (m, p)): + if p<500 and (m<5 and m>4): + return False + return True + +interlock1 = MyInterlock1() + +""" +#Motor group +class MyInterlock2(Interlock): + def __init__(self): + Interlock.__init__(self, (mg1, p1)) + + def check(self, ((m1,m2), p)): + if p<500 and (m1>4 and m2>4): + return False + return True + +interlock2 = MyInterlock2() +""" + + +""" +#Discrete Positioner +class MyInterlock3(Interlock): + def __init__(self): + Interlock.__init__(self, (dp1, p1)) + + def check(self, (dp, p)): + if p<500 and dp=="Out": + return False + return True + +#interlock3 = MyInterlock3() +""" \ No newline at end of file diff --git a/script/tutorial/devices.py b/script/tutorial/devices.py new file mode 100644 index 00000000..d2da8b88 --- /dev/null +++ b/script/tutorial/devices.py @@ -0,0 +1,195 @@ +import random +import ch.psi.pshell.device.DummyMotor as DummyMotor +import ch.psi.pshell.device.DummyRegister as DummyRegister +import ch.psi.pshell.device.DummyPositionable as DummyPositionable +import ch.psi.pshell.device.RegisterBase as RegisterBase +import ch.psi.pshell.device.MotorGroupBase as MotorGroupBase +import ch.psi.pshell.device.MotorGroupDiscretePositioner as MotorGroupDiscretePositioner +import ch.psi.pshell.device.ReadonlyRegisterBase as ReadonlyRegisterBase +import ch.psi.pshell.device.ReadonlyRegister.ReadonlyRegisterArray as ReadonlyRegisterArray +import ch.psi.pshell.device.ReadonlyRegister.ReadonlyRegisterMatrix as ReadonlyRegisterMatrix +import ch.psi.pshell.imaging.RegisterMatrixSource as RegisterMatrixSource + + +#################################################################################################### +# Simulated Devices +#################################################################################################### + +class AnalogOutput(RegisterBase): + def doRead(self): + return self.val if hasattr(self, 'val') else 0.0 + + def doWrite(self, val): + self.val = val + +class AnalogInput(ReadonlyRegisterBase): + def doRead(self): + time.sleep(0.01) + self.val = to_array(self.calc(), 'd') + return self.val + +class Waveform(ReadonlyRegisterBase, ReadonlyRegisterArray): + def doRead(self): + time.sleep(0.01) + self.val = to_array(self.calc(), 'd') + return self.val + + def getSize(self): + return len(self.take(-1)) #only reads if cache is None + +class Image(ReadonlyRegisterBase, ReadonlyRegisterMatrix): + def doRead(self): + time.sleep(0.01) + self.val = to_array(self.calc(), 'd') + return self.val + + def getWidth(self): + return len(self.take(-1)[0]) + + def getHeight(self): + return len(self.take(-1)) + + + +class Random(AnalogInput): + def calc(self): + return random.random() + + +class SinusoidSample(AnalogInput): + def calc(self): + self.x = self.x + 0.1 if hasattr(self, 'x') else 0.0 + noise = (random.random() - 0.5) / 10.0 + return math.sin(self.x) + noise + +class SinusoidTime(AnalogInput): + def calc(self): + noise = (random.random() - 0.5) / 10.0 + return math.sin(time.time()) + noise + + +class SinusoidWaveform(Waveform): + def calc(self): + ret = [] + x = random.random() + for i in range (20): + ret.append(math.sin(x)) + x = x + 0.1 + return ret + +class SinusoidImage(Image): + def calc(self): + (width, height) = (200, 100) + ret = [] + x = random.random(); + base = [] + for i in range (width): + base.append( math.sin(x)) + x = x + 0.05 + for i in range (height): + noise = (random.random() - 0.5)/5.0 + ret.append([x+noise for x in base]) + return ret + + +#Defintion +add_device(DummyMotor("m1"), True) +add_device(DummyMotor("m2"), True) +add_device(DummyRegister("reg1",3), True) +add_device(AnalogOutput("ao1"), True) +add_device(AnalogOutput("ao2"), True) +add_device(SinusoidSample("ai1"), True) +add_device(SinusoidTime("ai2"), True) +add_device(Random("ai3"), True) +add_device(SinusoidWaveform("wf1"), True) +add_device(SinusoidImage("im1"), True) +add_device(DummyPositionable("p1"),True) +add_device(MotorGroupBase("mg1", m1, m2), True) +add_device(MotorGroupDiscretePositioner("dp1", mg1), True) + + + +#Initial Configuration +p1.config.minValue = 0.0 #Not persisted +p1.config.maxValue = 1000.0 + +if dp1.config.positions is None: + dp1.config.positions = ["Park","Ready","Out","Clear"] + dp1.config.motor1 = ["0.0","4.0","8.0" ,"0.0"] + dp1.config.motor2 = ["0.0","5.0","3.0" ,"NaN"] + dp1.config.save() + dp1.initialize() + + + +#Update +m1.setMonitored(True) +m2.setMonitored(True) + + + + +#################################################################################################### +# Simple Readable / Writable objects can be created and used in scans +#################################################################################################### +class WritableScalar(Writable): + def write(self, value): + pass + +class ReadableScalar(Readable): + def read(self): + return random.random() + + +class ReadableWaveform(ReadableArray): + def getSize(self): + return 20 + + def read(self): + ret = [] + for i in range (self.getSize()): + ret.append(random.random()) + return ret + +class ReadableImage(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 + + + +ws1 = WritableScalar() +rs1 = ReadableScalar() +rw1 = ReadableWaveform() +ri1 = ReadableImage() + + +#################################################################################################### +# Imaging +#################################################################################################### + +configured = os.path.exists(Device.getConfigFileName("src1")) + +add_device(RegisterMatrixSource("src1", im1), True) +add_device(RegisterMatrixSource("src2", ri1), True) + +#Some configuration for so the imaging will work out of the box +if not configured: + import ch.psi.pshell.imaging.Colormap + src1.config.dataPolling = 100 + src1.config.colormapAutomatic = True + src1.config.colormap = ch.psi.pshell.imaging.Colormap.Temperature + src1.config.save() + src2.config.dataPolling = 100 + src2.config.colormapAutomatic = True + src2.config.save() + +