-
Notifications
You must be signed in to change notification settings - Fork 1
Scripting
Scripting in IBEX is done using genie_python. The genie_python reference manual gives a full account of what functions are available in genie_python. This page is intended to give a broad guide to scripting for the beginner and novice user.
- Running genie_python commands
- Common genie_python commands
- Converting Open Genie to genie_python
- Creating and running instrument scripts
- Creating and running user scripts
- Tips from the developers
- Some examples
There is also some specific scripting advice for the Muon Front End
If you are new to Python, the Mantid team has created an excellent Introduction to Python on the Mantid web-site.
When running genie_python
from an interactive console such as from
the GUI or after running C:\Instrument\Apps\Python\genie_python.bat
,
the genie
module will be aliased to g
. Genie commands can then
be accessed by using the prefix g.[COMMAND_NAME]
. For example:
g.start() g.cset("BLOCK_1",1) g.abort()
This is particularly useful from the GUI which will auto-complete commands and provide tool tips describing each function and its arguments.
Note that in many cases, arguments will be optional. For instance,
begin
can be used as g.begin()
despite supporting all of the
arguments period
, meas_id
, meas_type
, meas_subid
,
sample_id
, delayed
, quiet
, paused
, and verbose
.
Genie_python can be used in simulation mode. Simulation mode will allow you to run scripts but without changing items on your instrument. It does this by putting dummy commands in for some genie command, it also creates dummy blocks on the fly so that block values can be read and written to. NB This means that simulation can not be this will not validate your block names within the script. To run in simulation mode use the genie_python_simulate.bat
found in C:\Instrument\Apps\Python
.
Many genie_python
commands share the same name with their Open Genie
equivalent so it will often be very straightforward to find the function
you're looking for. Still, here is a list of the most commonly used
genie_python
commands. This is not a complete list. For full
information, consult the genie_python reference manual.
Command | Description | Example |
---|---|---|
begin | Starts a new run | g.begin() |
end | Ends the current run | g.end() |
abort | Aborts the current run | g.abort() |
pause | Pauses the current run | g.pause() |
resume | Resumes the current run after it has been paused | g.resume() |
Command | Description | Example |
---|---|---|
cget | Gets the useful values associated with a block | g.cget("NEW_BLOCK") |
cset | Sets the setpoint and runcontrol settings for blocks | g.cset("NEW_BLOCK",1) |
get_pv | Gets the value for the specified PV | g.get_pv("IN:INSTNAME:IOC_01:STAT") |
set_pv | Sets the value for the specified PV | g.set_pv("IN:INSTNAME:IOC_01:STAT",1) |
Command | Description | Example |
---|---|---|
get_uamps | Gets the current number of micro-amp hours | g.get_uamps() |
get_frames | Gets the current number of good frames | g.get_frames() |
get_runstate | Gets the current status of the instrument as a string | g.get_runstate() |
get_mevents | Gets the total counts for all the detectors | g.get_mevents() |
get_totalcounts | Gets the total counts for the current run | g.get_totalcounts() |
waitfor | Interrupts execution until certain conditions are met | g.waitfor("NEW_BLOCK",value=1) |
waitfor_block | Interrupts execution until block reaches specific value | g.waitfor_block("NEW_BLOCK",1) |
waitfor_time | Interrupts execution for a specified amount of time | g.waitfor_time(1) |
waitfor_frames | Interrupts execution to wait for number of total good frames to reach parameter value | g.waitfor_frames(1000) |
waitfor_uamps | Interrupts execution to wait for a specific total charge | g.waitfor_uamps(9.9) |
waitfor_runstate | Waits for a particular instrument run state | g.waitfor_runstate("paused") |
waitfor_move | Waits for all motion or specific motion to complete | g.waitfor_move("NEW_BLOCK") |
If you know a little bit of Python already, converting Open Genie
scripts to genie_python
is very often straightforward. Even if
you're new to Python, once you know a few simple pieces of syntax,
you'll be able to convert many scripts without issue.
Note: If you are new to Python, consult the Introduction to Python on the Mantid web-site.
We've included some common genie_python commands on this page, but for a full list refer to the genie_python reference manual.
One thing where there is no direct comparison between Python and Open Genie is with indentation.
In Python, different code blocks are identified by their indentation
level. In many programming languages, code blocks are encased in curly
braces ({...}
), but Python uses indentation. For example:
print "No indent" def my_func(): print "1 indent. Inside the function" if True: print "2 indents. Inside the if clause" print "2 indents. Still inside the if clause" print "1 indent. In the function but not the if clause" print "No indent. Outside the function"
Typically an indent
is 3 or 4 spaces. Tabs can be used too, but
Python won't recognise that a tab is the same as 4 spaces so can lead to
problems. It's often best to avoid them. This might be a new concept if
you've only ever written Open Genie, but trust us in that it opens up a
lot of ways to make scripts more powerful, easier to read and more
reliable.
Open Genie syntax |
genie_python syntax |
Comments |
---|---|---|
PROCEDURE my_func | def my_func(): | This is the standard way to define a function in Python |
begin | g.begin() | Many functions in genie_python have the same name as in Open Genie. In these cases, prepend g. to idicate the method belongs to genie_python and append () to tell Python to execute the method with no arguments |
my_func_2 100 | my_func_2(100) | To execute a function, give its name and then the argument in brackets. This is a user-defined function, not a genie_python function, so it has no g. in front. |
my_func_3 100 200 | my_func_2(100,200) | As above, except that multiple arguments are separated by a comma |
waitfor seconds=20 | g.waitfor(seconds=20) | Some methods can take named arguments. In these cases you simply give named arguments in the form [name]=[value] . Note that you can mix named and unnamed arguments, but unnamed arguments always appear at the start of the argument list. This section gives a more detailed description |
cset/nocontrol MY_BLOCK=value | g.cset('MY_BLOCK',value,runcontrol=False) | Some Open Genie commands take optional arguments. The same principle applies as in the above example. |
# This is a comment | # This is a comment | Comments are the same in both languages |
Different instruments may have specific scripts that are required for
multiple users. To make accessing these easier, genie_python
loads
instrument scripts at startup.
For the most part, this is the same as creating-user-scripts.
Instrument scripts should be placed in C:\Instrument\Settings\config\[MACHINE_NAME]\Python\inst
.
Everything in the instrument scripts will be executed when
genie_python
is started. This includes - Making functions available
to be called later - Setting variables - Running methods. Typically, most
code in instrument scripts will be contained within functions
(procedures in Open Genie language), but it's important to be aware that
anything that isn't will be included too. For example, if an instrument
script is loaded:
a = 1 def print_a(): print str(a)
then the user can call print_a
, but they can also use the variable
a
, and change its value, which may not be desirable. If instead the
script was:
def print_a(): a = 1 print str(a)
then the user could only access print_a()
and the value could not be
changed.
To reload the inst module, for instance if you had updated the script or added a new script, you should issue the command reload(inst)
.
Please note: When you are running scripts, the script is being executed on the instrument control PC. This means that the script itself has to be stored on the instrument control PC. It also means that any path names in your scripts refer to locations on the instrument control PC (unless you are referring to network drives).
Once the script is loaded, anything from the script will be available
using the inst
package reference. For instance if your script
contains the function my_function
you can call:
inst.my_function()
Whilst using the scripting perspective in the Ibex GUI, users will also
benefit from the same auto-complete feature as they do with
genie_python
commands.
- First, we need to create a script file. By default, user scripts
should be placed in
C:\scripts
. Navigate to your desired directory and create the script file with extension.py
. - Write some
genie_python
! - Save the file
We have glossed over step 2 because Python is a very powerful scripting language. Combined with Open Genie, the potential scope of a script is enormous, and well beyond the scope of this guide. For example though, here is a simple script that executes a calibration run.
# Change the title calibration_run_title = "Calibration run 1, 29th September" g.change(title=calibration_run_title) # Begin the run print "Beginning calibration run : " + calibration_run_title g.begin() # Wait for 100 uamps g.waitfor(uamps=100) # End the run g.end() print "Calibration run finished successfully"
Please note: When you are running scripts, the script is being executed on the instrument control PC. This means that the script itself has to be stored on the instrument control PC. It also means that any path names in your scripts refer to locations on the instrument control PC (unless you are referring to network drives).
Once you've created your script, it's time to run it. There are a number of ways of launching a Python script.
- Launch the Ibex GUI
- Navigate to the scripting perspective
- Run the command
g.load_script("C:\path\to\script\my_script.py")
where the path and script name are updated appropriately- Note that if you omit the absolute path to the file (i.e.
C:\path\to\script
) thengenie_python
will look in the current script directory. By default this isC:\scripts
but can be viewed and set with the commandsg.get_script_dir()
andg.set_script_dir()
respectively.
- Note that if you omit the absolute path to the file (i.e.
- When the script is loaded, any procedures in the script will be run automatically. If the script contains any function, you will now be able to call them from within the scripting window.
- Launch a
genie_python
terminal fromC:\Instrument\Apps\Python\
by runninggenie_python.bat
- Follow the above starting at step 3.
Even with correct syntax, a working script can become bug-prone and difficult for users to update. Here we document some tips to help keep your scripts working as expected and easy to modify in the future.
As this is Python, genie_python
conforms to the standard pattern of
calling Python functions. The arguments to the function are contained
within brackets and the variables passed in as a comma-separated list.
Ordering is important but can be overridden by using named variables,
for instance the following are all correct and equivalent:
g.change_beamline_pars("PAR1",1) g.change_beamline_pars(name="PAR1",value=1) g.change_beamline_pars(value=1,name="PAR1") g.change_beamline_pars("PAR1",value=1)
In the last example, named and unnamed variables are mixed. Unnamed variables must precede named variables. The following examples are not valid
g.change_beamline_pars(name="PAR1",1) # Named variable before unnamed g.change_beamline_pars(1,"PAR1") # Cannot change order of unnamed variables
Using named variables can be very useful in avoiding mistakes. For instance, getting the order of high and low limits the wrong way round. For instance this example:
g.change_monitor(1,10,0)
is wrong and wouldn't work. Instead, we could have written:
g.change_monitor(1,high=10,low=0)
which would have worked and makes it clear for whoever comes to edit the code in future (hint: that person might be you!).
Python console will display most output in black this is from the standard out but will display error information in red. Error information should be written to standard error using sys.stderr.write("errorn").
Sequentially sets a position (e.g. sample changer) and waits for a fixed number of uamps
from genie_python import genie as g def loop_over_samples(block_name, position_names, charge_to_wait_for): """ Begins a run. Loops over a set of sample positions. Ends the run :param block_name: The name of the block containing the sample position :param position_names: A collection of the positions to loop over :param uamps_to_wait_for: The number of uamps to wait for between samples """ g.begin() total_charge_to_wait_for = 0 for position_name in position_names: print "Moving sample changer to position: {0}".format(position_name) g.cset(block_name, position_name) total_charge_to_wait_for += charge_to_wait_for g.waitfor_uamps(total_charge_to_wait_for) g.end()
Reads the value of a block and moves it through a series of intervals to a new values taking a fixed time
from genie_python import genie as g def ramp(time, block_name, steps=100, final_value=0.0): """ Moves a block between an initial and target value over a sequence of steps, waiting a fixed time at each step. :param time: The overall time to take to ramp. Time will be split evenly between steps :param nsteps: The number of steps to take during the ramp :param block_name: The name of the block whose value to set :param final_value: The value we want to ramp to """ initial_value = g.cget(block_name)['value'] if initial_value is None: print "Unable to determine temperature from block {0}. No action will be taken".format(block_name) return def values_match(v1, v2): tolerance = 0.001 small = 1.0e-20 return 2*abs(v1-v2)/(abs(v1)+abs(v2)+small) < tolerance step_duration = time/steps for i in range(1, steps+1): step_value = initial_value + (final_value - initial_value)/steps*i g.cset(block_name, step_value) print "Ramping to: {0} Target value: {1}".format(step_value, final_value) g.waitfor_time(seconds=step_duration) new_block_value = g.cget(block_name)['value'] if not values_match(new_block_value, step_value): print "WARNING: The current value for block {0}, {1}, does not match the target value of {2}".format(block_name, new_block_value, step_value) print "Ramp complete"