SAS/AF Timer Class
SAS/AF lacks a Timer class.
This sample class demonstrates how a Timer class can be simulated using sleep() to time slice while waiting for events.
Run the example by issuing command:
AFA C=EXAMPLE.UTILITY.TIMERTEST.FRAMECopyright 2005 Richard A. DeVenezia This page was last updated 03 January 2005.
EXAMPLE.UTILITY.TIMER.CLASS
The Timer class schedules actions by using time slicing. A looping sleep occurs while there are no SAS/AF events pending. When an event is pending the looping stops. A Timer must be restarted after events have been serviced. Additionally, the looping will halt if an action returns a non-zero value.
SAS/AF applications are single threaded. Thus, only one Timer object can be active at a given time. The application is responsible for restarting the timer after an event has been serviced by the AF executor.
This class uses Action objects.
An Action object is any object that has:
- method
action()
- attribute
interval
- attribute
timeout
Actions are scheduled for execution by registering them with a call to
addAction()
.
An error will occur at runtime if an object registered for scheduling
does not have the requisite method and attributes.
action()
should execute quickly.
Timer is a crude emulation of a real timer (which is not
supported by AF executor) and
may be subject to stutters.
Author: | Richard A. DeVenezia |
Version: | January 4, 2005 |
Consider Action object SelfControl that subclasses a ProgressBar. When action() is called the bar will update. The bar will proceed from 0% to 100% every barTime seconds.
class example.utility.SelfControl extends sashelp.classes.ProgressBar_c ; public num interval; public num timeout; * progress bars are 0 to 100; * what time interval (seconds) is represented at 100 percent?; public num barTime / (initialValue=2); * percent of barTime interval; action: method return=num; value = mod (datetime(),barTime) * 100/barTime; endmethod; endclass;
Now use it in a frame. When the Start button is clicked Timer simulation will begin and the action method of SelfControl will be called every 5 milliseconds.
Init: declare Timer t = _new_ timer(); declare list attrs = { _region_ = { ulx=16,uly=1,lrx=215,lry=20,units='pixels' } , _attrs_ = { barStyle = 'Smooth' } }; declare SelfControl c1 = _neo_ SelfControl(attrs); c1.verticalPosition = 10; c1.barTime = 1; t.addAction (c1, 150); return; Start: rc = t.start(); return;
Every 5 milliseconds ? Take a closer look at Timer.
When addAction was called a SelfControl was added to queue actions.
Then start gets called. After a short nap the actions queue is iterated.
Each Action that has timed out has its action() called.
In the example interval is 150ms and slice is 5ms, thus after every 30 naps (give or take one) the bar updates. The bar progresses from 0 to 100 roughly every 6.66 updates (barTime/interval)
class example.utility.Timer; public num slice / ( initialValue=5 ); private List actions ; addAction: method _action:Object _interval:num ; if searchO (actions, _action) = 0 then insertO (actions,_action,-1); _action.interval = _interval; endmethod; start: method return=num ; declare num i; declare Object action; * set first timeouts; do i = 1 to listlen (actions) while (not event()); action = getItemO (actions,i); action.timeout = datetime() + action.interval / 1000; end; * wait for event pending due to user action; do until (event ()); sleep (slice,0.001); do i = 1 to listlen (actions) while (not event()); action = getItemO (actions,i); if datetime() >= action.timeout then do; action.action(); action.timeout = datetime() + action.interval / 1000; end; end; end; return 0; endmethod; endclass;