You can have radR run your own code from several places in the processing loop. These places are called hooks, and you can "hang" your own functions from them for radR to execute (morbid terminology acknowledged). These hook functions are also called hooks, when it isn't confusing.
Example:
Goal: have radR print the data timestamp to the console each time meta-data for a scan are read.
Solution:
rss.add.hook("SCAN_INFO", function(si) gui.print.cons(si$timestamp))
This is formatted as a single line to ease copy/pasting it into the radR window (don't forget to hit Enter).
Here is a detailed explanation:
rss.add.hook( ## call the function responsible for adding user hook functions to radR
"SCAN_INFO", ## the first parameter is the name of the hook; i.e. the location of the predefined
## point in radR processing from where your function will be called. This one
## corresponds to the point where radR has just finished reading the scan meta-data
## from its current data source, but before the scan data themselves have been read.
## (meta-data are things like timestamp, number of pulses, pulse length, and so on)
function(si) ## the next parameter is your function. In this case, the hook will call your function with a single
## parameter, namely the list with scan meta-data. Each hook calls its hook functions with
## a fixed, pre-determined list of parameters, but your function is free to ignore them by
## using "..." as its parameter list: function(...) instead of function(si)
gui.print.cons( ## this is the first (and only) statement of your function. If it is to have more than one
## statement, these must be surrounded in braces: function(...) { statements }
## Many built-in radR functions begin with gui. which means they are related to the user interface.
## gui.print.cons is a function whose job is to print an R object to the radR gui console.
## unix users can avoid this and simply use print to send the object to the terminal window.
si$timestamp ## this extracts the timestamp element from the meta-data item passed to your function.
## si is a list with named elements, and this element contains the date/time for the
## first pulse in the scan. This is stored internally as the number of seconds elapsed since
## the midnight between Dec. 31 1969 and Jan. 1, 1970 in the Greenwich timezone.
)) ## remember to close any parentheses opened earlier.
List of radR Hooks
The complete list of radR hooks is found in the file radR/main/radRshared.h Here is the list as of Nov., 2008:
ANTENNA_CONFIG
BEGIN_SCAN
BLIP
CLASSIFY
DONE_SCAN
END_SINK
END_SOURCE
FIND_PATCHES
FULL_SCAN
GET_SCAN_DATA
GET_SCAN_DATA_NAMES
GET_SCAN_INFO
ONEXIT
ONPAUSE
ONPLAY
ONSTOP
PAINT_BACKGROUND
PATCH
PATCH_STATS
PLOT_CURSOR_MOVED
POST_SCAN_CONVERT
PRE_SCAN_CONVERT
PUT_SCAN_DATA
RAW_BLIPS
SCAN_INFO
SCAN_ROW
SCORES
START_SINK
START_SOURCE
STATS
TK_PLOT_MODE
TRACK
UPDATE_PARMS
You can learn where these are called by examining the radR source code for lines like
rss.call.hook(RSS$PRE_SCAN_CONVERT_HOOK, ...)
TODO: document the location and calling conventions of each hook!!!
Useful Functions for Hooks
These are presented in the form of examples:
rss.disable.hook("SCAN_INFO") ## radR will stop calling the hook function you defined above
rss.enable.hook("SCAN_INFO") ## radR will resume calling the hook function you defined above
rss.drop.hook("SCAN_INFO") ## radR will delete (and no longer call, of course) the hook function you defined above
You can add multiple hook functions to a single hook. The different functions are distinguished by a name, which follows the name of the hook in most hook functions. By default, the name is "user". The order in which they are executed is random.
TODO: not exactly true, but we should move to a well-defined, user-specified order.
## add a hook function called myhook1 to the PRE_SCAN_CONVERT hook. It will assign the current
## time to the variable time.hook.last.called in the global environment (since we use <<- instead of <-).
rss.add.hook("PRE_SCAN_CONVERT", "myhook1", function(...) time.hook.last.called <<- Sys.time())
## disable the hook function myhook1 which is installed on the PRE_SCAN_CONVERT hook
rss.disable.hook("PRE_SCAN_CONVERT", "myhook1")
## completely remove the hook function myhook1 which is installed on the PRE_SCAN_CONVERT hook
rss.drop.hook("PRE_SCAN_CONVERT", "myhook1")
Where radR keeps hooks
The object RSS$hooks holds all hook functions (i.e. those which have been defined with rss.add.hook and not deleted with rss.drop.hook). Each element of RSS$hooks is a list of hook functions, stored by hook name.
e.g.
names(RSS$hooks) ## the list of all hook names
names(RSS$hooks$DONE_SCAN) ## the names of all hook functions installed on the DONE_SCAN hook
RSS$hooks$DONE_SCAN$saveblips$f ## the hook function installed on the DONE_SCAN hook by the saveblips plugin
RSS$hooks$DONE_SCAN$saveblips$enabled ## flag: is the saveblips plugin's DONE_SCAN hook function enabled?
Debugging hook functions
Unfortunately, this is currently dangerous in Linux and impossible in Windows.
In linux, you must call debug() with the installed version of the hook function:
debug(RSS$hooks$DONE_SCAN$saveblips$f)
If you try to do this:
f <- function(...) { gui.print.cons(Sys.time()); gui.print.cons("Hello from my hook")}
rss.add.hook("DONE_SCAN", f)
debug(f)
you will find that the debugger is never entered. This is because rss.add.hook installs a copy of your hook function, and the debugging flag must be set directly on that copy.
TODO: is there any way to have R disable the Tcl/Tk event loop upon entering a debug()ed function?