caputRecorder provides an EPICS database that contains the EPICS PV mentioned above, and other PVs that function as caputRecorder's user interface. The user-interface PVs are monitored by the python program caputRecorder.py, and direct its operation. When caputRecorder.py is given a function name and told to record, it begins monitoring the caput-notification PVs of specified IOCs, and writes python commands corresponding to each notification into the python program file macros.py.
caputRecorder.py also inspects the file macros.py and writes the names of functions in the file to a set of EPICS menus (that is, to mbbo records). A user can select a function name from those menus, and tell caputRecorder to execute that function.
Thus, each IOC is responsible for telling caputRecorder that it received a caput, and one IOC also tells caPutRecorder what to do.
EPICS IOCs that participate in caputRecorder, either by posting the caputs they receive, or by hosting caputRecorder's user interface, must be prepared as follows. (These are excerpts from the synApps xxx module, release R5-8, which is already configured to use caputRecorder.)
Add CAPUTRECORDER
to the IOC's RELEASE file
Include caputRecorder.dbd
in the IOC's .dbd file.
Link the IOC's code against the caputRecorder
library.
Load the database caputRecorder.db
, for example with this
excerpt from st.cmd:
dbLoadRecords("$(CAPUTRECORDER)/caputRecorderApp/Db/caputRecorder.db","P=xxx:,N=300")
where N
is the length of the longest caput-notification string
("pvname,value,user@host") that will be sent to the recorder.
If you're using autosave, you'll probably also want to include
caputRecorder_settings.req
in the IOC's
auto_settings.req
file:
file caputRecorder_settings.req P=xxx:,N=300If you do this, you must also add the following line to save_restore.cmd:
set_requestfile_path("$(CAPUTRECORDER)", "caputRecorderApp/Db")
registerCaputRecorderTrapListener('xxx:caputRecorderCommand')
where xxx:caputRecorderCommand
is the name of the PV to which
caput-notification strings will be written.
TRAPWRITE
for the desired users and hosts. Here is a sample
excerpt from st.cmd:
where the fileiocsh asSetFilename("$(TOP)/iocBoot/accessSecurity.acf") exit
accessSecurity.acf
might have the following content:
HAG(workstation) {mooneylinux.aps.anl.gov} UAG(user) {mooney} ASG(DEFAULT) { RULE(1,READ) RULE(1,WRITE,TRAPWRITE) { HAG(workstation) UAG(user) } RULE(1,WRITE) }This file grants write permission to anybody on any host, and specifies that TRAPWRITE is in effect only for user mooney on workstation mooneylinux. You probably don't want TRAPWRITE in effect for caputs from an IOC, but do want it for caputs from channel-access clients, such as MEDM, that aren't in an IOC.
Note that the order in which rules occur in the file matters for TRAPWRITE. If
RULE(1,WRITE)
were encountered beforeRULE(1,WRITE,TRAPWRITE)
, TRAPWRITE would not be in effect for anybody, because everybody is covered byRULE(1,WRITE)
.If you can't or don't want to select users and hosts using access-security rules, you can specify TRAPWRITE for all, and select users and hosts with caputRecorder's user interface. The simplest possible access-security file that will work for caputRecorder is the following:
ASG(DEFAULT) { RULE(1,WRITE,TRAPWRITE) }
caputRecorder.db
database displayed to the user via MEDM (or
caQtDM, or CSS-BOY). The Python program caputRecorder.py will also
monitor and write to this database.
start_putrecorder
in its top-level directory (which I'll call
xxx
), and the file macros.py in
xxx/xxxApp/op/python
. An example copy of
start_putrecorder
(from the synApps xxx module) is available as
caputRecorder/example_start_putrecorder
.
An example copy of macros.py is
available as caputRecorder/caputRecorderApp/op/python/example_macros.py
.
The button labelled "(re)start recorder" starts or restarts the python program caputRecorder.py
As implemented, the MEDM display presumes that the current working directory of the process running MEDM isxxx/xxxApp/op/adl
, and it invokes the shell script../../../start_putrecorder
to run the python programcaputRecorder/caputRecorderApp/op/python/caputRecorder.py
. The example version of start_putrecorder tries to ensure that only one copy of caputRecorder.py is running.
Inputs
The text-monitor field labelled "caput:" displays the PV name and value most recently written to the IOC. This field is written to by the access-security trap listener, and is updated whether or not a python function is being recorded.
The text-entry field labelled "comment:" allows you to add comments to the python function being recorded. This field is monitored only while a python function is being recorded.
The text-entry field labelled "delay" specifies the time delay in seconds
of the delay command that will be added to the macro being recorded when you
press the "Add" button. The actual python code for this command will be
something like time.sleep(1.200)
. After caputRecorder.py has
written the delay command, it will press the "Done" button.
The text-entry field labelled "time to wait for completion:" specifies the time in seconds that python will wait for completion of a macro command that does a caput to an EPICS PV, before going on to the next command. "Completion" means completion of any EPICS processing that results from the execution of that caput. It's ok to change this time while a macro is being recorded.
Record
The text-entry field labelled "function name" should contain the name of the python function you want to record. Note that you aren't permitted to use a function name that already exists in the macros.py file.
The choice button labelled "record puts" is used to start and stop recording.
Select
The button labelled "edit macros.py" invokes the command named by the environment variable EDITOR on the file macros.py.
Normally, you should not have to press the "Reload Macros" button, because caputRecorder.py reloads automatically after a new function has been recorded. However, if you edit macros.py manually, you must press "Reload Macros" to tell caputRecorder.py to reread the file. (If you deleted a function from macros.py, reloading isn't sufficient; you must restart caputRecorder.py, using the "(re)start recorder" button.)
The "Refresh Menus" button is needed whenever macros.py has changed, whether by recording a function, or by manually editing the file.
The text-monitor and text entry fields under the "arguments for selected function" label are the names and default values of the arguments, if any, of the selected function. As recorded, functions have no arguments, but you can edit the python code. If you do, you should use the "name=value" syntax for defining function arguments; otherwise your run-time user will not know which argument is which.
The menu fields labelled "menus of function names:" are the means by which a function is selected for playback. Currently the caputRecorder.db database supports only two menu fields, each of which can hold 16 function names, but there could be many more.
Playback
The text-monitor field contains the name of the selected function. To play it back (to cause python to execute it) press the "Do" button. When the function has finished, the "Do" button will return to the "Done" state. If you want to execute the function repeatedly, enter the number of times in the field labelled "times"
The "Abort" button is intended to stop an executing function, and also to do any other abort-related work specified in the function named "_abort" in macros.py. The supplied example_macros.py defines an abort function that aborts scans, halts motors, and stop the scaler named "scaler1".
caputRecorderUsers
. (See "Record caputs from whom?" in
the figure below.) To disable recording from a specific user, put a minus sign
in front of the username.
caputRecorderHosts
. (See "Record caputs from whom?"
in the figure below.) Only the first element of a host name need be specified. (For
example, "mooneylinux.aps.anl.gov" is used as "mooneylinux".)
caputRecorderPrefixes
. (See "Record caputs received by which IOCs?" in
the figure below.) Unlike user and host choices, this choice does not take
effect while a macro is being recorded. Also unlike user and host choices, this
choice is not about the sender of a caput, but about the recipient.
On startup, caputRecorder monitors the iocs whose prefixes are specified on the
command line. If caputRecorderPrefixes
is not empty, it will
override the initial list of iocs.
caputRecorder "reads" the macros.py file by importing it:
import macros.pyAfter macros.py has been changed, caputRecorder "rereads" it by reloading :
reload(macros)Reload will overwrite objects in the live macros module with values from the macros.py file, but it will not delete any objects. If you delete a function or global variable from macros.py, reloading won't get rid of it; you have to restart python, which you can do by pressing the "(re)start recorder" button.
caputRecorder records a caput with the following python code:
epics.caput("xxx:scan1.P1PV","xxx:m2.VAL", wait=True, timeout=300.0)This code will request a completion callback from EPICS, and will wait as long as 300 seconds for the callback to arrive. This is probably much longer than most operations will take to complete, but five minutes is not long enough for many scans. If you use caputRecorder to execute scans, you'll probably want to increase the "time to wait for completion" described in the "How to use"/"Inputs" section.
Currently, it is not an error for a macro to timeout while waiting for a callback. Python will simply move on to the next command and execute it. Currently, caputRecorder does not take advantage of PyEpics' ability to execute several commands and then wait for all to complete (that is, the "use_complete" option of the PV class's put() method).
caPutLog can also trap caputs and write them to a PV. A proof-of-principle implementation of caputRecorder used caPutLog 3.4 to do this, but it didn't handle long strings.