how to update instance var with timed frequency

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

how to update instance var with timed frequency

Frank Sonnemans-3
Hello All,

I am working on a laser simulator, which consists of the usual MVP
components, a Laser model, a LaserShell presenter and the associated view.

The laser model has a shotcounter which needs to be incremented with the
  repetition frequency of the laser, typically between 1 and 200 Hz. At
the same time the view should be updated continuously with the current
value of the shotcounter. Furthermore the process needs to terminate
once the laser stop method is called.

So in pseudo code:

start
        "Start the laser"
        begin-loop
                self incrementcounter
                self trigger #counterUpdated
                wait (1 / self frequency) "wait fraction of second"
                (self stopFlag) ifTrue: [Terminate loop]
        end-loop "loop until stop method is called"

I have three problems:

1. I don't know how to construct the loop with a timed wait condition in
the background (so that the rest of the app continues).

2. I don't know how the best way to interrupt the loop.

3. I am concerned that the UI cannot handle the potential 200 event
triggers per second.

Any ideas?

Thanks in advance,



Frank


Reply | Threaded
Open this post in threaded view
|

Re: how to update instance var with timed frequency

Ian Bartholomew
Frank,

> 1. I don't know how to construct the loop with a timed wait condition in
> the background (so that the rest of the app continues).
>
> 2. I don't know how the best way to interrupt the loop.

The attached (Dolphin 4)  package should show one way of doing this. There
are other ways but, IMHO, this is about the simplest.

Controls -
Firing rate - the interval between shots in mS. Dynamic, can be altered on
the fly.
Start - start firing
Stop - stop firing
Shots fired - cumulative shots fired.
Shots/sec - average number of shots per second (see below)

The Shell subclass is not particularly interesting - it just starts, stops
the model and reports the results.

The model's #start method is where the looping is done. A block is forked
that increments the counter and then waits for the appropriate interval.
This loop is controlled by an instVar that is set to true while the model is
running. Setting the instVar to false (in the #stop method) stops the loop
running and the forked process terminates.

As the block is forked the normal UI keeps running so the shell is updated
and the buttons still work even though the model is in a loop.

The #finalize bits are to ensure that if the process is always terminated,
even if the shell is closed while the model is still running.

> 3. I am concerned that the UI cannot handle the potential 200 event
> triggers per second.

The attached package has an additional button that runs the model for 10
seconds to show the number of shots that can be achieved in that time
period.  I expected to find a point where you could move from an accurate
shot/sec to one that wobbles due to the inner forked loop not having enough
time to update. It doesn't appear to work like that though, inaccuracy
creeps in at rates that shouldn't pose a problem.

The strange thing is that if you prevent the #firedCounter method triggering
the #fired event, so the UI is not updated on every shot and the only work
performed is therefore to update the instVar, the same inaccuracies occur.

Changing the forking priority doesn't seem to make a difference either (in
case anyone was wondering).

I'm a bit stumped by this. I don't think I'm doing anything wrong but it's
not something I've noticed before.   It looks like overhead introduced by
the forked loop but seems a bit excessive to me?  I'll have another check
later.

Have a look anyway and see if the package helps

Ian














begin 666 Las.zip
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
M;Y,%>4/-!4F@HQ"N1]*8K]4FKSVN1X;R`S)08 ;9G@\Y2WX'_>&[#RG*'(#
M$@(8"\5/%]%N_L;1I3:T%3^6-P[\$>0<"'(PX]M:@=4A6ZYSJCVD-];7@V\A
M<!PI)%O(TT5<6[Z+<=,JA8UQK9$6(B)@5AW//?)$:2*@AF"FP%^(WBK>=;0<
M]8G>&*[2';1>> *1-JZKB>/WO9%G(_=E76N=6(!#VEBJ>@EG(+TL\]D9Z/D3
MZMYV)2O'63T^5GG!XYQ'/;N-Y\&@_5Q'-90?OD.UJ1D'J$A8W3RWH>4BY@XB
MAK,($.C![*!FS_[E^8_4#I#HU,@<@7:H(U513RQKUR@%EOI"&>KZHB_&0HC0
MA;5 !,(2CG#%"/Z/W3K:Z]<<6]EG+^R43<D-M[0>YX;*EE"APN':H7G!5:8$
MK=[E' BL]B<W2WF]]J:RD:P!RY9JI++DH35OJ=9@:]0VNSVRW)ZRMF'M'4/=
M083N6]F4WRPM2[?&1]'IV3W4YVW!.N2JM6I<QSK[59=G*4UT7 B[QJ5L:U"Z
MCJ^_AF_#+*(;:'("]?Q4^.DI/S!^.(W7Q2=<6-K*@25\J#V CSNUT!?NL^/$
M;N%D0"IO:$^$D[&]I#*U;Q4/HQW7/O;?4^-A'=]M>&"ZY\:#IZTGW/]H/"A^
MN-+*/96Y:UN97RO:FGD/SC3UN7>F#=N?^KA^6F*$[[R/494%5B7<][D"*XN]
M8E^E&O-_<.:HMBUSZCJC/OL*:<%(/F_U-Y]I]<JH/?-C[0?5@:=GJJUPCN/5
M^?'5%)^IFG+KF9O:(ZMI. X=M>N@.P3:=<!Y@\X=8]@/XZZ8]L8;F%+[VMEN
MJ5WUNC/%B+-@D7ODR9VS7665KR*[G.?Z*4G("D.W*;;)ZRZ(ZWDCU[$QVTB#
M!^-AQTK*YPS>F?OL`:7GOI54QX>E['CJ7._5B%S3GBC7(3B;,:7:_RJCU^T4
M](QF?ZW8*5#_NGSE]<%5.T3?L3EW875DKL^=I9Z6I8]=[_F<O'<U-WV\[\6?
M`\$1VQ?-FQ:^F^&V>X;;O#>6XPOQJ]LC,]RV0X$9+CQ3T+VCTHNJ^0C^X5V(
M$()NMO%.I^^;?%/"/+?60[GJP:J@C[CUR-J=D^I_,+(XXQ^)++[ATL<'"A&.
MRD6.T\YKA8HJWO (O'6SX-P>;+[762=C*WJ7O,V<GANQCJ+G'30C;5_$ZD@-
ME!U/C;H>L=[0G@AUMEWOP*FMS19%1T@*_:5^5_5_:RCBD^JN*&*_Z_)"M0*M
M0\8ZNJW(T/S'?)X+%[[BRK@(^.W+(W$Q4!C_6O@P\=W IO:U\<$K#:S$WM@!
MM^@WZ2Z-V'BF#Y$MSK'E[;\#4?G&<#-0-Z'K<./RVY%1W47Q>"ANUM%MQ<UN
M7E7<GPM-([X'4_0]J[V?VQ=-7_'T$IJ;VB-1]/]R>EEW[[_B]$(4#\[6YB\1
MY.DEE#G*')\K0SF^?'//)Q=.L'TSU%[TM\=5'83=O$LC:K[H>TYSQU[/YR_X
M=MHRZS>]\D8"]Z/R5/-U?T%BJ3RV5-:*/IU#\ VU36]KZ08-;YKP#(I:B@#H
M'>J'.O20]]8LBS6!NN72>T]Z3UV/JKBM^"W+V"/?D@?#V@_J73?PP9U]H/\R
M"/3M>_+MZLI?B-0>4,]>RS\>^,^!T#H"I O4ANDZQ_+'F/\%4$L!`A0+% ``
M``@`K*0I+-$5N^V9"P``;S<```<``````````0`@`````````$QA<RYP86-0
52P4&``````$``0`U````O@L`````
`
end


Reply | Threaded
Open this post in threaded view
|

Re: how to update instance var with timed frequency

Andy Bower
In reply to this post by Frank Sonnemans-3
Frank,

I'm not sure whether this will help, but have you taken a look at the
Bouncing Balls sample. If it's not already in your image you can find the
package at:

http://www.object-arts.com/Lib/Downloads/4.0/BouncingBalls.zip

This was originally created to illustrate the use of a DoubleBufferedView to
provide flicker-free animation. However, you'll also see that
BouncingBallView starts a background process to update the animation a
certain number of times a second (set by default to 30 in #frameRate). Take
a look at BouncingBallView>>initialize and BouncingBallView>>onDestroyed to
see how the background process is started and stopped.

You shouldn't have any problem triggering 200 (or many more) event triggers
per second providing the event handlers don't do too much work. As to
whether you'd be able to update the display at 200Hz this will depend on how
fast your machine is and how complex the update process. Personally, I would
have thought that forcibly updating the screen 200 times a second is a bit
pointless since no human could discern the changes that fast. You could
probably just #invalidate the view (or a particular area of it) and let
Windows do the actual painting at it's leisure when the normal WM_PAINT
messages come in.

Best Regards,

Andy Bower
Dolphin Support
http://www.object-arts.com
---
Are you trying too hard?
http://www.object-arts.com/Relax.htm
---

"Frank" <[hidden email]> wrote in message
news:[hidden email]...

> Hello All,
>
> I am working on a laser simulator, which consists of the usual MVP
> components, a Laser model, a LaserShell presenter and the associated view.
>
> The laser model has a shotcounter which needs to be incremented with the
>   repetition frequency of the laser, typically between 1 and 200 Hz. At
> the same time the view should be updated continuously with the current
> value of the shotcounter. Furthermore the process needs to terminate
> once the laser stop method is called.
>
> So in pseudo code:
>
> start
> "Start the laser"
> begin-loop
> self incrementcounter
> self trigger #counterUpdated
> wait (1 / self frequency) "wait fraction of second"
> (self stopFlag) ifTrue: [Terminate loop]
> end-loop "loop until stop method is called"
>
> I have three problems:
>
> 1. I don't know how to construct the loop with a timed wait condition in
> the background (so that the rest of the app continues).
>
> 2. I don't know how the best way to interrupt the loop.
>
> 3. I am concerned that the UI cannot handle the potential 200 event
> triggers per second.
>
> Any ideas?
>
> Thanks in advance,
>
>
>
> Frank
>


Reply | Threaded
Open this post in threaded view
|

Re: how to update instance var with timed frequency

Blair McGlashan
In reply to this post by Frank Sonnemans-3
Frank

You wrote in message news:[hidden email]...

>
> I am working on a laser simulator, which consists of the usual MVP
> components, a Laser model, a LaserShell presenter and the associated view.
>
> The laser model has a shotcounter which needs to be incremented with the
>   repetition frequency of the laser, typically between 1 and 200 Hz. At
> the same time the view should be updated continuously with the current
> value of the shotcounter. Furthermore the process needs to terminate
> once the laser stop method is called.
> ....[snip]...
> I have three problems:
>
> 1. I don't know how to construct the loop with a timed wait condition in
> the background (so that the rest of the app continues).
>
> 2. I don't know how the best way to interrupt the loop.
>
> 3. I am concerned that the UI cannot handle the potential 200 event
> triggers per second.

You have basically two choices.
1) Use a Windows timer - these can be set up so that Windows sends a message
at regular intervals to a window through the message queue.
2) Use a background Smaltlalk Process (really a "thread") in conjunction
with Delays

Given the rate of update and some other considerations, I would go for (2).
Now Smalltalk events are not really intended for cross-thread use - if you
fork off an update process in the background which triggers events to do the
update you will run into thread synchronisation issues. There is also the
issue of keeping up with the synchronous updates. This may or may not be a
problem, but generally speaking it is best to design the solution so that
the UI makes best efforts to keep up with the updates in an asynchronous
fashion that does not allow it to get behind. So based on your pseudo code
you might want something like:

 clockProcess :=
   [self startLaser.

   [
   [self incrementCounter.
   view invalidate.
   (Delay forMilliseconds: 1000 / self frequency) wait]
     repeat]
     ifCurtailed: [self stopLaser]]
     forkAt: Processor userInterruptPriority

In this case 'view' is a variable referencing the View object that is
displaying the shot counter. The important point is that the #invalidate
message instructs that view only that it needs to repaint, without actually
waiting for it to repaint at that time. The view can then update in an
asynchronous fashion when there is CPU time available to do so. Obviously
the 'view' will need access the current value of the shot counter in order
to repaint itself (and furthermore access to that shot counter and the
#incrementcounter operation will need to be atomic - you can use a Mutex
object to protect against more than one thread accessing it at once). If the
GUI is not able to keep up with every change in the counter, this will not
really matter since when it is able to repaint it will use the current value
of the counter, whatever thay may be at the time.

Note that the frequency will not be absolute if arranged like this, since it
does not account for the time taken to execute the loop, and also the wait
period of a delay is the minimum period. If, for example, the OS has
switched control to another task, then the loop may not start running again
until Dolphin regains control, even though the Delay has expired. Over time
it will operate at a lower frequency than specified. To be more accurate one
needs to get the current time and adjust the length of each delay to keep
the loop working at the right frequency. You can work out how to do that
yourself, but as a hint you'll probably need to use Time
class>>millisecondClockValue (or even #microsecondClockValue!).

As for how to stop the loop-counting process, just send it a #terminate
message. The use of #ifCurtailed: guarantees that #stopLaser (or whatever
other cleanup you want to do) is run.

Regards

Blair