startfilesystemmonitor
NAME
,
PROPERTY
,
VALUE

The startfilesystemmonitor statement sets up and starts a repeating task that will be performed whenever a file and/or folder is modified.


Parameters

This statement has three parameters:

name – specifies a name for this periodic task. The name must be unique. If the task is specific to a database or a window we recommend using a heirarchical name in the format task_database_window, for example Intake_Images_Catalog

property – a timer property. Each timer has properties including the task to be run, the interval, etc. See below for detailed descriptions of the different timer properties available. Properties can be specified in either upper or lower case, for example INTERVAL or interval.

value – the new value for the property specified by the previous parameter.


Description

This statement sets up and starts a repeating task that will be performed that will be performed whenever a folder is modified. For example, this code will watch the newimages folder (in the Desktop folder), and if a new JPG or PNG file is dropped into the folder, it will be adde to the database.

startfilesystemmonitor "dropimages",
   "watch",info("desktopfolder")+"newimages",
   "gate","new",
   "extensions",".jpg.png",
   "code",{
       find Image=_fs_monitor_path
       if info("found")=false()
           addrecord
           Image = _fs_monitor_path
       endif
    }

If you later wanted to stop this timer, you would use this code:

stopfilesystemmonitor "dropimages"

To change a monitor, simply start it again with different options.

You can set up multiple monitors for different tasks. Each monitor works independently, so that they don’t interfere with each other. Note: Monitors will continue watching for changes even if Panorama is not the frontmost application.

Monitor Properties

Each monitor has multiple properties – what file or folder should be watched, what code is used to perform the monitor’s task, whether or not the monitor runs in the background, etc. Wnen you start a monitor, you can specify all the properties you need, like this:

startfilesystemmonitor name,property1,value1,property2,value2,property3,value3

Most properties have a default value. The two properties you must set up are WATCH and CODE.

Watch

This property specifies the folder or file that will be monitored. Any activity in this folder (or any subfolders) will cause the monitor to run. Don’t specify a folder with a huge number of subfolders or you may generate so many events that Panorama can’t keep up (for example, you probably wouldn’t want to monitor your entire user folder). It is possible to use multiple watch options to monitor multiple items with the same code, like this:

startfilesystemmonitor "drop",
    "watch",info("picturesfolder"),
    "watch",info("moviesfolder")

This example will watch for changes in both the Pictures and Movies folders. Or, you can also watch multple items by supplying a carriage return delimited list of items to watch, like this:

startfilesystemmonitor "drop",
    "watch",info("picturesfolder")+cr()+info("moviesfolder")

Extension(s)

If one or more extensions are specified, only files with those extensions will be flagged by the monitor. To specify multiple extensions, separate them by a period.

startfilesystemmonitor "drop",
    "watch",info("picturesfolder"),
    "extensions","jpg.jpeg.png.tiff"

It is also allowed to include a leading period, for example ".jpg.jpeg"

Exclude

You can exlcude files or folders within a watched folder, like this:

startfilesystemmonitor "drop",
    "watch",info("picturesfolder"),
    "exclude",info("picturesfolder")+"Private"

This will monitor the Pictures folder, but will ignore any changes in the Pictures/Private folder.

You can exclude multiple items by providing multiple exclude options, or by providing a carriage return delimited list of items to exclude.

Code

Use this property to define the code that will run when a watched folder or file is modified. This property is text, so you’ll need to quote the entire thing with alternate quote characters, for example brackets or pipes.

startfilesystemmonitor "drop",
    "watch",info("picturesfolder"),
    "code",|||nsnotify "File has changed!","TEXT",filename(_fs_monitor_path)|||

You use any kind of code to perform a task, but you should keep the code as minimal as possible. If the code takes too long to run, it could interfere with normal Panorama usage. You normally wouldn’t want the monitor code to do anything that affects the user’s use of Panorama. For example, it’s usually not a good idea to have a window suddenly open or close for no apparent reason.

By default, you cannot rely on Panorama being in a certain state when the task begins. For example, you should not rely on a particular window or database being active when the timer runs (there are some exceptions, see the scope and background properties below). It’s even possible to set up a monitor that will keep running even if the database that started it has closed.

Panorama sets up several local variables with information about the file or folder that has been modified. The two most important variables are _fs_monitor_path and _fs_monitor_event.

The _fs_monitor_path variable will contain the path and filename of the file or folder that has changed. So if you wanted to examine, copy or modify the file (or folder) that has changed, this variable lets you do that.

The _fs_monitor_event will contain the type(s) of modification that occured, and the type of item that was modified (file, folder, etc.) For example if a file was renamed, the _fs_monitor_event value would be:

ItemRenamed+File

The most common modifications that can occur are:

Additional modifications that can occur are (you may want to ignore these):

The possible item types are:

You will often see multiple modifications in one transaction. For example, this event occurred after using BBEdit to create a new text file.

ItemCreated+ItemInodeMetaMod+ItemModified+ItemFinderInfoMod+ItemXattrMod+ItemCloned+File

Technical Note: Panorama’s file system monitoring is based on an underlying Apple API called FSEventStream. If files are being modified rapidly, FSEventStream may not be able to keep up. If that happens the modification type will begin with kFSEventStreamEventFlag, for example kFSEventStreamEventFlagKernelDropped. If you see a modification type with this prefix, there may have been one or more missed file modifications. You may want to fully scan the watched folder yourself in that case (probably using the filecatalog( function). You can also look for additional information in Apple’s developer documentation, but for some of these modification types there isn’t any, and even when there is you may not find it helpful. Best bet is to do a complete rescan if you observe one of these modifications and need to make sure you never miss any change.

There are also three additional local variables with additional information about the file modification event:

Scope

Monitors operate in one of three different scopes: global, database, and window.

Database is the default scope. This means that the monitor is attached to the database that was current when the monitor was started. If the database is closed, the monitor stops and is deleted. If the database is not the current database, the monitor pauses (unless the background property is enabled, see below).

If a monitor is operating using window scope that means the monitor is attached to the window that was current when the monitor was started. If the window is closed, the monitor stops and is deleted. If the window is not the current window, the monitor pauses (unless the background property is enabled, see below).

If a monitor is operating using global scope the monitor is not attached to any database or window. The timer will keep running until Panorama quits or until you explicitly stop it. Be careful when writing the code for global monitor – you can make absolutely no assumptions about what database or window is open and frontmost (if any) when the monitor code runs.

Background

When a monitor is operating in either database or window scope, the monitor normally won’t run when the database or window is not the active database or window. However, if the background property is true, the monitor will always run, even when some other database or window is active. Before running the monitor code, Panorama will automatically set up a secret window for the database associated with the monitor.

In this example, the monitor uses database scope because that is the default. Whenever any image is added to the Pictures folder (see the gate option described later on this page), the monitor code will add a record to the database and save the path of the file into the ImagePath field.

startfilesystemmonitor "drop",
    "watch",info("picturesfolder"),
    "extensions","jpg.jpeg.png.tiff",
    "gate","new",
    "background","yes",
    "code",|||addrecord ImagePath = _fs_monitor_path|||

This monitor will continue collecting images even when some other database is active – the database that is collecting data could even be invisible with all windows closed!

Error

If an error occurs while a monitor is running, the monitor is stopped and a notification is displayed in Notification Center. Use this parameter to change the notification option. The available options are notify, alert and console. Notify, the default, displays the error message in Notification Center. The Alert option stops and displays an alert with the error message. The Console option doesn’t display any visible message, but outputs the error to the system log. You can use Apple’s Console application (in the Application/Utilities folder) to view this log. This example shows how to send timer error messages to the console.

starttimer ...,"error","console"

However, a better approach is to handle the error yourself with if error or try/catch (see Error Handling).

Gate

You can use one or more gate options to set up a “gate” that only lets thru certain types of modifications and/or certain types of modified items. You could do the same thing yourself in the monitor code (by examining the _fs_monitor_event variable), but using a gate is much faster and better for overall performance.

For example, suppose you are only interested in when a file is created or modified. You’re not interested in folders, and don’t care about a file being renamed, deleted, or anything else. To do this, set up three gates as shown in this code:

startfilesystemmonitor “drop”, “watch”,info(“picturesfolder”), “gate”,“create”, “gate”,“modify”, “gate”,“file”, “code”,{ …. }

The monitor code will only be executed if a modication event makes it through the specified gates. In this example, the event must be create or modify, and it must be a file.

The possible modification gates that can be set up are:

The possible item type gates are:

If you set up any gates, each modification event must match at least one of the gates you’ve set up or the modification event will be ignored. If you don’t set up any modification gates, then all modification types will be processed. If you don’t set up any item type gates, then all types of items will be processed. For example, if you want to process all renames, whether files or folder, just set up one gate:

"gate","rename"

Or if you just want to know about changes to files, but not folder, again only one gate is needed.

"gate","file"

Or, you can combine both modification and item type gates. This will process all new files, while ignoring all other modifications.

 "gate","new","gate","file"

Remember of course that “all modifications” means only within the watched folder(s), not on your entire hard drive.

Stream

This option modifies how the system tracks file system changes. For most applications the defaults are best, but if necessary you can tweak the tracking. You can specify one or more stream options, each separated by a + symbol, like this:

"stream","nodefer+fileevents"

The options can be in upper or lower case. The available options are listed below. (These option descriptions may seem a bit technical because they are mostly copied directly from Apple’s technical documentation.)

nodefer – This option affects the meaning of the latency parameter (see below). If you specify this flag and more than latency seconds have elapsed since the last event, your app will receive the event immediately. The delivery of the event resets the latency timer and any further events will be delivered after latency seconds have elapsed. This flag is useful for apps that are interactive and want to react immediately to changes but avoid getting swamped by notifications when changes are occurringin rapid succession. If you do not specify this flag, then when an event occurs after a period of no events, the latency timer is started. Any events that occur during the next latency seconds will be delivered as one group (including that first event). The delivery of the group of events resets the latency timer and any further events will be delivered after latency seconds. This is the default behavior and is more appropriate for background, daemon or batch processing apps.

ignoreself – Don’t send events that were triggered by the current process (Panorama). This is useful for reducing the volume of events that are sent, but if you need to track file modifications made by Panorama itself, you should not include this option. (If you don’t specify any options, the ignoreself option is set by default, so file or directory changes made by Panorama will not be tracked.)

fileevents – If this option is set, changes to both files and directories will be tracked. Otherwise, only changes to directories are tracked.

Latency

This is the number of seconds the service should wait after hearing about an event from the kernel before passing it along to Panorama. Specifying a larger value may result in more effective temporal coalescing, resulting in fewer callbacks and greater overall efficiency. The default value is 3 (seconds).

If you want the most rapid response, set the latency to 1 second and use the nodefer option described in the previous section.

Example: Monitoring an External Editor

To illustrate how file system monitoring can be used, lets look at an example of monitoring changes made in an external editor. For this example there are two files, the database that we will be using and a text file.

Opening the text file in BBEdit looks like this:

The database has a form with a Text Display object that uses the fileload( function to display the contents of the text file.

Of course by itself, this Text Display object won’t update if the text file is edited. To make that happen, a file system monitor needs to be added. This monitor is very simple, it only monitors one specific file (note that the folder doesn’t need to be specified because it is in the same folder as the database).

The code of this monitor is very simple – just one showvariables statement. The variable xesync doesn’t actually exist, but if you look back to the Text Display object you’ll see that its formula references this variable (inside of a catcherror( function so that no error will be displayed because of the non-existant variable). So the result of showvariables statement is simply that the Text Display formula will be recalculated, and the display will update.

The end result is that if the text is edited in BBEdit and saved, a few seconds later the display in Panorama will automatically update.

Edit again in BBEdit and save, and again Panorama will update a moment later. It doesn’t matter where or how the text is modified.

A final touch is to automatically start up the monitor when the form is opened. See Implicitly Triggered Procedures to learn more about how Form Event Procedures work.


See Also


History

VersionStatusNotes
10.2NewNew in this version.