Tutorial: Introduction to File Event Scripting


Mira's File Event Scripting is a powerful feature used for running real time, script-driven processing of data files. One common application is for processing, analyzing, or measuring images in real time as their files are saved into a folder. This tutorial shows how to use the File Event Scripting facility to monitor a star across successive image to measure changes in brightness.

The discussion below uses 3 sample scripts included with Mira Pro Ultimate Edition:

     File Watcher - init.lua

     File Watcher.lua

     File Watcher - final.lua

These scripts are installed in the Scripts\Samples folder (see Mira's Special Folders). You also can open them by using the File > Open method or by using the Script Manager. As described in the File Event Scripting command topic, these scripts are used in the following order: to initialize the process, perform repeated real-time processing, and to finalize or "clean up" the task at the end. This tutorial starts by showing how to configure the event processing, then discusses the scripts themselves. Near the end of the script, you will run the event processing loop and drag image files into the target folder to see the event processing in action. To understand the code in the scripts, you may wish to open the Mira Pro Script User's Guide as a companion to this tutorial. (Web version)

Creating Working Folders

First create a new "working" folder in a separate location from where the sample scripts are installed. Then copy the 3 scripts named above into the working folder. In this example, we assume the new folder is on the default drive, the C: drive. Go to the C: drive and create the following folder: C:\Scripts. Now create a folder where image files will be stored. This folder should be named C:\Images.

NOTE: Usually, it is a better strategy to use a different drive for storing the script and images, such as H:, instead of the default C: drive.

Configuring the Procedure

Open the File Event Scripting dialog by clicking the btn_event_file.png button on the main toolbar. The dialog contains 4 text fields where file names will be stored. Each text field has a [...] button at its right end which opens a standard File Open dialog for choosing the file to load into the text field. Using these [...] buttons, select the folder and script files as shown below. Also make sure the Initial Script and Final Script boxes are checked so these scripts will be executed.

The dialog has 3 buttons named Text which are used to set text values that will be sent to each script before it is executed. This allows you to specify parameter that are retrieved by the script. In this example, the Initial Script Properties give the starting (column,row) coordinates of the star being monitored, plus information used by the aperture photometry calculations. Click the Text button for the Initial Script and enter all the values shown below:

The Properties in this window specify the Properties described in the table below. In other applications, your actual Properties would depend on what type of information you need to pass to the script.

Initial Script Text Properties

296.454

Column coordinate, in pixels.

329.092

Row coordinate, in pixels.

4

Inner measuring aperture radius, in pixels.

10

Inner sky sampling radius, in pixels.

15

Outer measuring aperture radius, in pixels.

0

Aperture background calculation mode (0 = median).

4

Centroid calculation radius, in pixels.

15

Centroid tracking radius, in pixels.

22.5

Photometric zero point (used to get a positive magnitude somewhere near the correct value).

This tutorial is based on the BL-CAM*.fts series of sample images provided with Mira. The starting (column,row) position usually is determined from a sample image acquired before the File Event Scripting process is started. In this example, the target star is located at pixel coordinates (296.454, 329.092) in the image BL-CAM.fts. The the main responsibility of the Initial Script will be to store these Properties in the Registry so they can be loaded by the Event Script each time it runs on an image. This is discussed below.

Selecting Events to Process

Next we need to configure the event processing. The Options button opens the Event Handling Options dialog where you select the type of file event and setup an optional execution delay to wait between discovering the file event and triggering the event script. In this example, you want to trigger the event script when a file is saved into the watched folder ("File Added") and you will use a 100 millisecond delay. Click Options and configure the dialog as shown below.

In this example, no text Properties are used by the Event Script or Final Script, so those text boxes can be empty. In this case, the Event and Final scripts do not access any text Properties so it doesn't actually matter if there are values in those particular text boxes.

Saving the Configuration

Finally, after setting up the configuration, you should save it so a profile so that you can he entire configuration later from among other saved profiles. To save the configuration, enter a name into the Profile Control at the top of the dialog, then click the profile button and select Save as shown below. Here we give the profile the name "Star Monitor".

At this point we have a fully configured procedure. Now let's examine how the 3 scripts are used to do event processing.

The Initial Script

The Initial Script performs any necessary configuration at the beginning of the folder watching process. This script is called only once, usually in order to initialize variables used by the event script. The "File Watcher - init.lua" script is listed below. The script comments generally explain what the script does. It's main purpose is to load the text Properties passed from the Initial Script Text dialog, then parse them into values that are stored in the Registry for use by the Event Script.

Here is an overview of how the Initial Script works: The GetScriptString() function fetches the Properties passed to the script in a data string, the StrTok() function converts that string to individual strings and stores them in an indexed table, then CRegistry class methods are used to store each of the individual strings to a named registry entry.

--////////////////////////////////////////////////////////////////////////

--////      Called once from the "File Event Scripting" task to initialize the

--////      Properties that will be used when called to process a file event.

 

-- The string passed to the script contains Properties to setup that control the event script.

-- Use a tokenizer to parse the script string into the substrings str[1], str[2], etc.

str = StrTok( GetScriptString() )

 

--      Save Properties in the Registry for use by the event handling script

--      count the number of strings in the str table to see if there are enough; if not, exit.

if table.getn(str) < 9 then Exit("Not enough parameter strings sent to Initial Script\n") end

 

Reg = CRegistry:new("Real-time-phot")

Reg:SetNum( "CoordX", tonumber(str[1]) )

Reg:SetNum( "CoordY", tonumber(str[2]) )

Reg:SetNum( "Ap1", tonumber(str[3]) )

Reg:SetNum( "Ap2", tonumber(str[4]) )

Reg:SetNum( "Ap3", tonumber(str[5]) )

Reg:SetStr( "ApBg", str[6] )

Reg:SetNum( "CentRadiusX", tonumber(str[7]) )

Reg:SetNum( "CentRadiusY", tonumber(str[8]) )

Reg:SetNum( "ApZP", tonumber(str[9]) )

 

Reg:SetWnd( "PlotWindow", nil )      -- CRITICAL! If plotting results, initialize the plot window here

Reg:SetStr( "Status", "started" )      -- optional, just to keep a record of the processing

-- end of script

 

--[[ -- unused code to list the substrings in str{} To enable, remove the --[[ and --]]

n = table.getn(str)

for i = 1, n do

if str[i] == nil then break end      -- test for bad string

     Printf("str[%d]: %s\n", i, str[i] )      

end --]]

 

Note: Below, there will be an option to use Version 2 of the Event Script to plot the results. Therefore it is absolutely critical that you include the line shown in the listing above: Reg:SetWnd( "PlotWindow", nil ). The Version 2 script will not work if the plot window is not initialized.

The bottom 6 lines of the Initial Script show a block of commented-out code bounded by --[[ and --]] ( Lua syntax for a block comment). This code lists all the parameter values sent to the script. If you want to test that the script unpacked the correct values sent from the Initial Script Text window (see above), remove --[[ and --]] to enable the code.

Looking forward to the first recorded event, the Event Script will start by calling saved parameter values from the Registry, then makes its computations and show results. As part of its processing, the script updates the star coordinates and saves them back to the Registry for use when it runs again on the next event. This updating allows small changes in the star position to be tracked from one image to the next.

The Event Script

The Event Script is executed when triggered by a file event. The "File Watcher.lua" script listed below makes an aperture photometry measurement on a star each time an image file is stored in the watched folder to trigger a "File Added" event, as selected in the Options dialog. This script loads parameter values from the Registry in order to make the measurement. When the processing is completed, the script stored updated values back to the Registry for use on the next image that triggers a File Added event. This technique is used to allows the coordinates to track motion of the target star between images. Two alternative scripts are provided: The first version lists the magnitude values in a Mira text editor. The second version graphs the magnitude versus Julian Date.

Version 1:

--////////////////////////////////////////////////////////////////////////

--////      Called from the "File Event Scripting" task to process a file

--////      when a file event occurred in the file watching loop.

 

-- The string passed to the script contains the file's full path and other Properties

--      separated on separate lines (see below).

 

-- Use a tokenizer to parse the script string into the substrings.

--      str[1] = full path name of the file

--      str[2] = the event - possible values are "Added", "Deleted", "Renamed" (note: mixed case)

--      str[3] = full path of the new file name, used only if str[2] == "Renamed"

--      str[4], str[5], etc. contain extra text passed to the script

 

-- Use the tokenizer function "StrTok" to parse the string into substrings. In this

--            case, only the string is passed, hence the delimiter defaults to "\n\r\t" (new line, carriage return, and tab)

str = StrTok( GetScriptString() )

 

-- continue only if str[2] == "Added" to indicate that this script runs on an image that was just added

if str[2] ~= "Added" then Exit() end

 

-- assign str[1] to another variable (purely cosmetic).

strImageFileName = str[1]

 

-- Create class objects to use. Do this before changing any parameter values (see below)

 

-- open the selected file and then process it

I = CImage:new()      -- create a CImage object to hold the image

if I:Open( strImageFileName ) == false then

     sErrMsg = Sprintf( "Cannot open '%s' as an image.\n", strImageFileName )

     Exit( sErrMsg )

end

 

P = CPlotView:new()      -- create a CPlotView object for plotting

A = CApphot:new()      -- create a CApphot object for doing the photometry

C = CCentroid:new()      -- create a CCentroid object for centroiding the coordinates

 

-- load aperture photometry Properties from the image (exptime, gain, etc.)

A:GetImageParams( I )

 

-- open the Registry for loading Properties

Reg = CRegistry:new("Real-time-phot")

 

-- centroid Properties:

x = Reg:GetNum( "CoordX", 1 )

y = Reg:GetNum( "CoordY", 1 )

C:SetSample( Reg:GetNum( "CentRadiusX", 5 ), Reg:GetNum( "CentRadiusY", 5 ) )

-- photometry Properties:

A.nRadius1 = Reg:GetNum( "Ap1", 5 )

A.nRadius2 = Reg:GetNum( "Ap2", 10 )

A.nRadius3 = Reg:GetNum( "Ap3", 15 )

A:SetBgMethod( Reg:GetStr( "ApBg", "mode" ) )

nZeroPt = Reg:GetNum( "ApZP", 22 )

 

-- update the object coordinates using the centroid

bSuccess = C:Calc( I, x, y )      -- compute the centroid position

if bSuccess == false then      -- if false, then the centroid calculation failed... should we exit?

     sErrMsg = Sprintf("Centroid Error processing '%s', returned X=%lg Y=%lg.\nSkip this image?\n", I:Path(30), C.x, C.y )

     bExit = GetYesNo( sErrMsg )      -- query the user whether to abort

     if bExit == true then Exit() end

end

 

-- make the magnitude measurement:

A:Measure( I, C.x, C.y )      -- measure the magnitude using the refined centroid coordinates

Printf( "%-30.30s\tMag = %lg +/- %lg, BG = %lg (sdev=%lg), SNR = %lg\n", I:Path(30), A.nMag+nZeroPt, A.nMagErr, A.nBgValue, A.nBgValueErr, A.nSnRatio )

 

-- update the object coordinates in the Registry for the next image

Reg:SetNum( "CoordX", C.x )

Reg:SetNum( "CoordY", C.y )

 

I:Close() -- Important: Close this image to free memory.

-- end of script

 

The first version of the event script saves the results to a text window — which may be completely sufficient for a simple application. It can be enhanced by graphing the Magnitude versus Julian Date ("JD") using information in the image header to calculate the JD. Here is a second, more complex version of the Event Script:

Version 2:

--////////////////////////////////////////////////////////////////////////

--//      Computes the magnitude and plots it in an existing plot window

--//                  Called from the "File Event Scripting" task to process a file

--//                  when a file event occurred in the file watching loop.

 

-- The string passed to the script contains the file's full path and other Properties

--      separated on separate lines (see below).

 

-- Use a tokenizer ("StrTok") to parse the script string into the substrings.

--      str[1] = full path name of the file

--      str[2] = the event - possible values are "Added", "Deleted", "Renamed" (note: mixed case)

--      str[3] = full path of the new file name, used only if str[2] == "Renamed"

--      str[4], str[5], etc. contain extra text passed to the script

 

-- Use the tokenizer function "StrTok" to parse the string into substrings. In this

--            case, only the string is passed, hence the delimiter defaults to "\n\r\t" (new line, carriage return, and tab)

str = StrTok( GetScriptString() )

 

-- continue only if str[2] == "Added" to indicate that this script runs on an image that was just added

if str[2] ~= "Added" then Exit() end

 

-- assign str[1] to another variable (purely cosmetic).

strImageFileName = str[1]

 

-- Create class objects to use. Do this before changing any parameter values (see below)

 

-- open the selected file and then process it

I = CImage:new()      -- create a CImage object to hold the image

if I:Open( strImageFileName ) == false then

     sErrMsg = Sprintf( "Cannot open '%s' as an image.\n", strImageFileName )

     Exit( sErrMsg )

end

 

-- define a helper function here to calculate the fractional Julian Date for the image

--/////////////////////////////////////////////////////////////////

-- returns the fractional Julian date for the image I

function FracJD( I)

     if I == nil or I.class ~= "CImage" then return 1 end      -- returns 1 if a CImage was not passed.

     local JD = CalcJD( I:DateStr(), I:TimeStr() )      -- Geocentric JD

     -- if you want Heliocentric JD instead, use the following code and comment-out the previoous line:

     --local JD = CalcHJD( I:DateStr(), I:TimeStr(), I:KwdGetHMS("RA"), I:KwdGetDMS("DEC") )

     return JD - math.floor(JD)      -- Return just the fractional part (note after 7AM in EST zone)

end

 

-- back to the main script

A = CApphot:new()      -- create a CApphot object for doing the photometry

C = CCentroid:new()      -- create a CCentroid object for centroiding the coordinates

 

-- load aperture photometry Properties from the image (exptime, gain, etc.)

A:GetImageParams( I )

 

-- open the Registry for loading Properties

Reg = CRegistry:new("Real-time-phot")

 

nCount = Reg:GetNum( "Count", 0 )      -- number of poitns plotted

nCount = nCount + 1                        -- increment the point counter

 

-- centroid Properties:

cx = Reg:GetNum( "CoordX", 1 )

cy = Reg:GetNum( "CoordY", 1 )

C:SetSample( Reg:GetNum( "CentRadiusX", 5 ), Reg:GetNum( "CentRadiusY", 5 ) )

C:SetTracking( Reg:GetNum( "CentTrackingX", 2*5 ), Reg:GetNum( "CentTrackingY", 2*5 ) )

-- photometry Properties:

A.nRadius1 = Reg:GetNum( "Ap1", 5 )

A.nRadius2 = Reg:GetNum( "Ap2", 10 )

A.nRadius3 = Reg:GetNum( "Ap3", 15 )

A:SetBgMethod( Reg:GetStr( "ApBg", "mode" ) )

nZeroPt = Reg:GetNum( "ApZP", 22 )

 

-- update the object coordinates using the centroid

bSuccess = C:Calc( I, cx, cy )      -- compute the centroid position

if bSuccess == false then      -- if false, then the centroid calculation failed... should we exit?

     sErrMsg = Sprintf("Centroid Error while processing '%s', returned X=%lg Y=%lg.\nSkip this image?\n", I:Path(30), C.x, C.y )

     bExit = GetYesNo( sErrMsg )      -- query the user whether to abort

     if bExit == true then Exit() end

end

 

-- make the magnitude measurement:

A:Measure( I, C.x, C.y )      -- measure the magnitude using the refined centroid coordinates

x = FracJD( I )            -- fractional Julian Date as calculated above

y = A.nMag + nZeroPt      -- y: magnitud plus a zero point to make the number nice

ye = A.nMagErr            -- errorbar: the magnitude uncertaninty

 

-- plot the photometry on an existing plot. Do this "long hand" to show how the script works:

 

P = CPlotView:new()      -- create a new CPlotView to manage the plot

 

Wnd = Reg:GetWnd( "PlotWindow" )      -- fetch the handle of the existing plot window from the Registry

P:SetWnd( Wnd )      -- attach the fetched window handle to this CPlotView object

 

P:Add( x, y, ye )-- add the x value, magnitude value, and an error bar to the plot data

 

if Wnd == nil then      -- true first time through (creates a new window)

     P:PlotPoints( "Julian Date", "Magnitude", "Light Curve", "Monitor" )      -- new plot window

else

     P:AddPoints()      -- after the first time through: add points to existing plot window

end

 

-- update the Registry for use with the next image

Reg:SetNum( "CoordX", C.x )      -- centroid x

Reg:SetNum( "CoordY", C.y )      -- centroid y

Reg:SetNum( "Count", nCount )      -- number of points plotted

Reg:SetWnd( "PlotWindow", P:GetWnd() )      -- CRITICAL! Save handle so same plot used next time

 

I:Close() -- Important: Close this image to free memory.

-- end of script

 

At the end of the "Version 2" Event Script above, be absolutely sure to save the handle of the plot window to the Registry, as in the following line: Reg:SetWnd( "PlotWindow", P:GetWnd() ).

The Final Script

The Final Script is executed when event monitoring is terminated. It's purpose is to finalize the processing, which may include things like updating the Registry for the next time you run the same profile. The "File Watcher - final.lua" script is shown below. In this example, it is used to update the status in the Registry, stating that the script completed normally and setting the handle of the plot window to "nil" so that a new plot window will be created next time this profile is run.

--////////////////////////////////////////////////////////////////////////

--////      Called once from the "Watch Files and Process" task to do clean-up

--////      when the file watching loop is terminated.

 

-- do any needed clean-up...

Reg = CRegistry:new("Real-time-phot")

Reg:SetStr( "Status", "completed" )

Reg:SetWnd( "PlotWindow", nil )      -- initialize the CPlotView window

-- end of script

 

Executing the Event Processing Loop

To test the script, you will drop a series of images into the C:\Images folder (on whatever disk drive you selected, instead of C:). To do this you need to open 2 instances of Windows Explorer. Then do the following:

  1. In the first instance of Windows Explorer, open the folder where your Mira sample images are installed.

  2. In the second instance of Windows Explorer, open the watched folder, here C:\Images.

  3. In Mira, click the Start button on the File Event Scripting dialog. This executes the Initial Script using the Properties specified in the Initial Script Text window.

NOTE: If you hear a "ding" immediately after clicking [Start], the script aborted. Usually this means that the initial script is invalid, failed, or could not be located. It also could mean that the watched folder does not exist. An error message is describes the problem.

At this point, the "Star Monitor" profile is multitasking, running as a separate thread to allow you to use other Mira capabilities while file events are triggered and handled. The process currently is waiting to receive a file event. So, the next step is to create a file event!

Creating File Events

  1. In the first instance of Windows Explorer, mouse-down on the file named BL-CAM.fts, drag it to the second instance of Windows Explorer, and drop it into the C:\Images folder. This creates a file event, which calls the Event Script.

  2. Repeat step 1 for each one of the BL-CAM*.fts images. There are 5 such images provided in the Mira Sample Images folder. It is important that you use each image in the order implied by its file name, starting with BL-CAM.fts and ending with BL-CAM-5.fts, as this is the time order in which the images were acquired.

Each time you drop a file into the C:\Images folder, a message is listed in the File Events text area of the File Event Scripting dialog. The message lists the time the event occurred, the event, and the name of the file that caused the event to fire:

           

Note that the text listed in the File Events box can be copied and pasted into a Mira Text Editor or other software (editor, spreadsheet, etc) that accepts text. You can select either a block of text or the entire contents. To copy the entire contents, right-click inside the File Events box and choose Select All from the menu. Then right-click to re-open the menu and select Copy to save the text to the Windows Clipboard. Open the target editor and paste the clipboard text into it.

In this example, you are using "Version 1" of the event script, so results are listed in a Mira Text Editor. From there, the results can be copied, printed, etc. In the picture below, "Mag" refers to the magnitude estimate, "BG" is the background value estimated under the star, and "SNR" is the Signal-to-Noise Ratio for the star. The information listed and its formatting is controlled by code in the Event Script.

The "Version 2" Event Script plots the data instead of listing it in a text window. If you repeat this exercise using the "Version 2" script rather than "Version 1", you will get the result shown below. Before switching to the "Version 2" script, be sure to drag the images back to their original folder C:\Scripts so you can then again drop them into the watched folder.

Finishing Up

When you have finished dragging and dropping the sample images into the C:\Images folder, click Stop on the File Event Scripting dialog. This calls the Final Script and terminates the process normally.

Related Topics

Contents, Tutorials, File Event Scripting, Aperture photometry, Contents