Page 1 of 1

[module] - Tiny module to create periodic tickers

Posted: Fri Jan 31, 2025 5:41 pm
by miso
Plain and simple, modularized for easy implementation.

Code: Select all

;-===MODULE DESCRIPTION===
;Tiny module to create periodic tickers
;Created with PB 6.20 Beta 1
;Author:      :miso
;Creation date:2025.01.31
;Purpose      :for periodic tasks like sending a keepalive packet to push through nat or change frame of an animation, etc
;Scope        :all platforms, but tested only on Windows 7 64 bit
;Permissions  :777, do what you want, (author takes no responsibilities nor any claims)

EnableExplicit
;-===MODULE DECLARATION===
DeclareModule ticker
  ;-===CONSTANTS===
  #TICKS_FOREVER = -10
  #MAXIMUM_ALLOWED_TICKERS = 255
  
  ;-===STRUCTURES===
  Structure tickerstructure
    timeout.i
    lasttick.i
    current_tick_count.i
    target_tick_count.i
    alive.i
  EndStructure

  ;-===PUBLIC PROCEDURE DECLARATIONS===
  Declare.i create(ID.i,ticktime_ms.i,number_of_ticks.i=#TICKS_FOREVER)
  Declare.i triggered(ID.i)
  Declare.i kill(ID.i)
EndDeclareModule

;-===MODULE===
Module ticker
  ;-===PRIVATE GLOBALS===
  Global Dim tickers.tickerstructure(#MAXIMUM_ALLOWED_TICKERS)
  ;-===PUBLIC PROCEDURES===
  
  ;***************************************
  ;Create a new ticker
  ;***************************************
  Procedure.i create(ID.i,ticktime_ms.i,number_of_ticks.i=#TICKS_FOREVER)
    If ID.i>=0 And ID.i<=#MAXIMUM_ALLOWED_TICKERS
      With tickers(ID.i)
        \lasttick.i = ElapsedMilliseconds()
        \timeout.i = ticktime_ms.i
        \current_tick_count.i = 0
        \alive.i = #True
        \target_tick_count = number_of_ticks.i
      EndWith
      ProcedureReturn #True
    EndIf
    ProcedureReturn #False
  EndProcedure
  
  ;***************************************
  ;Returns the triggered status of a ticker
  ;***************************************
  Procedure.i triggered(ID.i)
    If ID.i<0 Or ID.i>#MAXIMUM_ALLOWED_TICKERS
      Debug Str(ID.i) + " not exists, cant be checked"
      ProcedureReturn #False
    EndIf
    
    If tickers(ID.i)\alive <> #True
      ProcedureReturn #False
    EndIf
    
    If ElapsedMilliseconds()-tickers(ID.i)\lasttick<tickers(ID.i)\timeout
      ProcedureReturn #False
    EndIf
    
    tickers(ID.i)\current_tick_count + 1
    tickers(ID.i)\lasttick = ElapsedMilliseconds()
    If tickers(ID.i)\current_tick_count = tickers(ID.i)\target_tick_count
      tickers(ID.i)\alive = #False
    EndIf
    
    ProcedureReturn #True
  EndProcedure
  
  ;***************************************
  ;Sets a ticker to be dead
  ;***************************************
  Procedure.i Kill(ID.i)
    If ID.i<0 Or ID.i>#MAXIMUM_ALLOWED_TICKERS
      ProcedureReturn #False
    EndIf
    tickers(ID.i)\alive = #False
    ProcedureReturn #True
  EndProcedure
EndModule

;-===EXAMPLE USAGE===
OpenConsole("Periodic Tickers")
ticker::create(0,5000)   ;this ticker can proc any number of times with 5000 ms delay
ticker::create(1,1000,2) ;This can proc only twice, then stops
ticker::create(2,200)    ;this ticker can proc any number of times with 200 ms delay

Repeat
  If ticker::triggered(1) : PrintN("Tiktak") : EndIf
  If ticker::triggered(2) : PrintN("Tak")    : EndIf
Until ticker::triggered(0)
ticker::kill(0) ;not needed, just showcase for example
End



Re: [module] - Tiny module to create periodic tickers

Posted: Sat Feb 01, 2025 1:46 pm
by STARGĂ…TE
The module serves its purpose but it is inaccurate in time when the loop do also other tasks.

When you ask for the trigger with "ElapsedMilliseconds() - tickers(ID.i)\lasttick < tickers(ID.i)\timeout",
usually the time difference between lasttick and ElapsedMilliseconds() is larger then the timeout itself, when the trigger is activated.
However, later you set tickers(ID.i)\lasttick = ElapsedMilliseconds(), which meas, all triggers are slightly longer than then timeout and you accumulate an error.
It is more accurate to set tickers(ID.i)\lasttick = tickers(ID.i)\lasttick + tickers(ID.i)\timeout
and check also for multiple ticks between two calls of triggered(), when the program has some other delays.

Re: [module] - Tiny module to create periodic tickers

Posted: Sat Feb 01, 2025 2:02 pm
by miso
You are absolutely right, it is not for tasks that are perfect time sensitive. Sending a keepalive packet that is delayed a little would not make any difference.
It is more accurate to set tickers(ID.i)\lasttick = tickers(ID.i)\lasttick + tickers(ID.i)\timeout
Your insight is good again, as usual. If you use this in a threaded environment, it would be better in your way. However, in a non threaded environment if the main task took long, that way you can trigger the tickers multiple time one after another. That would not be better.

Everything depends on what it is used for. Your insight is appreciated and valuable though.