Someone requested a script that checked a database value over a period of time. The more time that passed, the more frequently the script had to check the value. For example, for the first 10 minutes check every 20 seconds, for the next ten minutes check every 10 seconds. The user needed to be alerted the first time the value did not match the desired value and the script needed to time out after a while if the values did not match during the defined period.
Here is my solution:
set timeoutUser to 5 -- minutes until script times out
set intervals to {8, 6, 4, 2} -- seconds
set tiers to {1, 2, 3, 525949} -- minutes
set timeoutScript to (timeoutUser * 60)
set myTrigger to false -- condition matched through database query
set time0 to (current date)
set intervalN to 1
repeat
set waitTime to (current date) - time0
if myTrigger = true then
exit repeat
-- If myTrigger is not executed, tell user to wait ONCE
else if waitTime < item 1 of intervals then
display dialog "Please wait for myTrigger to return true" buttons {"Cancel"} default button "Cancel" giving up after 3
-- myTrigger never executed, tell user script timed out
else if waitTime ≥ timeoutScript then
display dialog "Timed out" buttons {"Cancel"} default button "Cancel"
end if
-- Delay logic
if waitTime / 60 > item intervalN of tiers then
set intervalN to intervalN + 1
end if
delay item intervalN of intervals
display dialog (item intervalN of intervals) buttons {"Cancel"} default button "Cancel" giving up after 1
end repeat
display dialog "MATCH!" buttons {"Cancel", "OK"} default button "OK"
I thought an interesting idea for a script would be:
Given a period and min & max frequency values, have the script increase frequency in proportion to the remaining time. If anyone is interested, I’d like to see some of your ideas.
For “watch” situations over prolonged periods of time, you should check out ‘idle’ handlers. (As it happens, I’ve just posted a script containing one here.)
Until I implement Nigel’s suggestion, here is a script that increases frequency in proportion to the remaining time.
set timeoutUser to 1 -- minutes until script times out
set {maxInterval, minInterval} to {10, 1} -- seconds between tests
set timeoutScript to timeoutUser * minutes
set myTrigger to false -- condition matched through database query
set time0 to (current date)
repeat
set waitTime to (current date) - time0
if myTrigger = true then
exit repeat
-- If myTrigger is not executed, tell user to wait ONCE
else if waitTime < maxInterval then
display dialog "Please wait for myTrigger to return true" buttons {"Cancel"} default button "Cancel" giving up after 3
-- myTrigger never executed, tell user script timed out
else if waitTime ≥ timeoutScript then
display dialog "Timed out" buttons {"Cancel"} default button "Cancel"
end if
-- Delay logic
set myDelay to ((timeoutScript - waitTime) / timeoutScript) * maxInterval
if myDelay < minInterval then set myDelay to minInterval
delay myDelay
display dialog myDelay buttons {"Cancel"} default button "Cancel" giving up after 1
end repeat
display dialog "MATCH!" buttons {"Cancel", "OK"} default button "OK"
It has to be a stay-open application in order to use an ‘idle’ handler. The idea is that instead of having a script (run by some application) looping interminably and executing a ‘delay’ command for much of that time, you have an applet which sits there doing precisely nothing until it receives an ‘idle’ event. It executes its ‘idle’ handler once in response and then goes back to doing nothing. The handler should return a number when it exits. If it was the operating system which sent the event (it usually is), it’ll interpret the number as the number of seconds to wait before sending another such event to the applet.
Any initialisation can be done in the applet’s ‘run’ handler, which of course only responds to the initial ‘run’ event.
on run
-- Any initial stuff here.
end run
on idle -- Executed whenever the applet receives an 'idle' event.
-- Look at the situation now.
-- Act or not as required.
-- return a number telling the system how many seconds to wait before sending the applet another 'idle' event.
end idle
Is an idle event any event you declare within an if statement in idle handler or something else?
Would you provide an example of an event not sent by the operating system (meaning return 3 won’t wait 3 seconds?)
How do you work past the condition in the idle handler once it is satisfied to the rest of the script? For example, how would you say 4 after 3 below?
property idleCount : 0
global idleEvent
on run
-- Do stuff
set idleEvent to true
say 4 -- continue rest of the script after condition in idle handler is met
end run
on idle
if idleEvent = true then -- A condition to test in the middle of the script
set idleCount to idleCount + 1
say idleCount
if idleCount = 3 then set idleEvent to false
return 1
end if
end idle
Sorry. I still haven’t made myself clear. They get sent anyway. This from the AppleScript Language Guide:
So when the script application receives an ‘idle’ command, it executes its ‘idle’ handler. You don’t have to do anything about that.
Well another script could theoretically tell the script application to ‘idle’. The ‘idle’ handler would then execute and the other script would receive whatever’s returned. The other script might not be written to respond to this in the same way as the system. (This wouldn’t affect the ‘idle’ commands being sent anyway by the system.)
The idle handler is the rest of the script. Saying “4” would have to be written into it.
A quick’n’dirty adaptation of your script at the top of this thread would be:
global timeoutUser, intervals, tiers, timeoutScript, myTrigger, time0, intervalN, waitTime
on run
set timeoutUser to 5 -- minutes until script times out
set intervals to {8, 6, 4, 2} -- seconds
set tiers to {1, 2, 3, 525949} -- minutes
set timeoutScript to (timeoutUser * 60)
set myTrigger to false -- condition matched through database query
set time0 to (current date)
set intervalN to 1
end run
on idle
set waitTime to (current date) - time0
try
if (waitTime > 2) then display dialog (item intervalN of intervals) buttons {"Cancel"} default button "Cancel" giving up after 1
if (myTrigger) then
display dialog "MATCH!" buttons {"Cancel", "OK"} default button "OK"
error number -128
-- If myTrigger is not executed, tell user to wait ONCE
else if waitTime < item 1 of intervals then
display dialog "Please wait for myTrigger to return true" buttons {"Cancel"} default button "Cancel" giving up after 3
-- myTrigger never executed, tell user script timed out
else if waitTime ≥ timeoutScript then
display dialog "Timed out" buttons {"Cancel"} default button "Cancel"
end if
-- Delay logic
if waitTime / 60 > item intervalN of tiers then
set intervalN to intervalN + 1
end if
on error number -128
-- A "Cancel" button has been clicked above or this is the deliberate error after the "Match!" dialog.
quit -- Quit on the next idle.
return 1 -- Please send the next idle as soon as possible.
end try
return item intervalN of intervals
end idle