In object-oriented programming an object is active when its state depends on clock. Usually an active object encapsulates a task that updates the object's state. To the outer world the object looks like a normal object with methods that can be called from outside. Implementation of such methods must have a certain synchronization mechanism with the encapsulated task in order to prevent object's state corruption.

A typical instance of an active object is an animation widget. The widget state changes with the time, while as an object it has all properties of a normal widget.

The task

Implement an active integrator object. The object has an input and output. The input can be set using the method Input. The input is a function of time. The output can be queried using the method Output. The object integrates its input over the time and the result becomes the object's output. So if the input is K(t) and the output is S, the object state S is changed to S + (K(t1) + K(t0)) * (t1 - t0) / 2, i.e. it integrates K using the trapeze method. Initially K is constant 0 and S is 0.

In order to test the object:

 set its input to sin (2π f t), where the frequency f=0.5Hz. The phase is irrelevant.
 wait 2s
 set the input to constant 0
 wait 0.5s
Verify that now the object's output is approximately 0 (the sine has the period of 2s). The accuracy of the result will depend on the OS scheduler time slicing and the accuracy of the clock.

 
double MainTime
 
'===============
class RingMaster
'===============
'
indexbase 1
sys List[512] 'limit of 512 objects per ringmaster
sys max,acts
'
method Register(sys meth,obj) as sys
  sys i
  for i=1 to max step 2
    if list[i]=0 then exit for 'vacant slot
  next
  if i>=max then max+=2
  List[i]<=meth,obj
  return i 'token for deregistration etc
end method
'
method Deregister(sys *i)
  if i then List[i]<=0,0 : i=0
end method
'
method Clear()
  max=0
end method
'
method Act() 'called by the timer
  sys i,q
  for i=1 to max step 2
    q=List[i]
    if q then
      call q List[i+1] 'anon object
    end if
  next
  acts++
end method
'
end class
 
 
'=================
class ActiveObject
'=================
'
double     s,freq,t1,t2,v1,v2
sys        nfun,acts,RingToken
RingMaster *Master
'
method fun0() as double
end method
'
method fun1() as double 
  return sin(2*pi()*freq*MainTime)
end method
'
method func() as double
  select case nfun
    case 0 : return fun0()
    case 1 : return fun1()
  end select
  'error?
end method
'
method TimeBasedDuties()
  t1=t2
  v1=v2
  t2=MainTime
  v2=func
  s=s+(v2+v1)*(t2-t1)*0.5 'add slice to integral
  acts++
end method
'
method RegisterWith(RingMaster*r)
  @Master=@r
  if @Master then
    RingToken=Master.register @TimeBasedDuties,@this
  end if
end method
'
method Deregister()
  if @Master then
    Master.Deregister RingToken 'this is set to null
  end if
end method
'
method Output() as double
  return s
end method
'
method Input(double fr=0,fun=0)
  if fr then freq=fr
  nfun=fun
end method
 
method ClearIntegral()
  s=0
end method
'
end class
 
 
'SETUP TIMING SYSTEM
'===================
 
extern library "kernel32.dll"
declare QueryPerformanceCounter (quad*c)
declare QueryPerformanceFrequency(quad*f)
declare Sleep(sys milliseconds)
end extern
'
quad scount,tcount,freq
QueryPerformanceFrequency freq
double tscale=1/freq
double t1,t2
QueryPerformanceCounter scount
 
macro PrecisionTime(time)
  QueryPerformanceCounter tcount
  time=(tcount-scount)*tscale
end macro
 
 
'====
'TEST
'====
 
double       integral
double       tevent1,tevent2
RingMaster   Rudolpho
ActiveObject A
'
A.RegisterWith Rudolpho
A.input (fr=0.5, fun=1) 'start with the freqency function (1)
'
'SET EVENT TIMES
'===============
 
tEvent1=2.0 'seconds
tEvent2=2.5 'seconds
'
PrecisionTime t1 'mark initial time
MainTime=t1
'
'
'EVENT LOOP
'==========
'
do
  PrecisionTime t2
  MainTime=t2
  if t2-t1>=0.020 'seconds interval
    Rudolpho.Act 'service all active objects
    t1=t2
  end if
  '
  if tEvent1>=0 and MainTime>=tEvent1
    A.input (fun=0) 'switch to null function (0)
    tEvent1=-1      'disable this event from happening again
  end if
  if MainTime>=tEvent2
    integral=A.output()
    exit do 'end of session
  end if
  '
  sleep 5 'hand control to OS for a while
end do
 
print str(integral,4)
 
Rudolpho.clear