This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Timers (Searching, killing, using time binds)

Issues often discussed about Tcl scripting. Check before posting a scripting question.
Post Reply
User avatar
Sir_Fz
Revered One
Posts: 3793
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Timers (Searching, killing, using time binds)

Post by Sir_Fz »

Catching if a certain timer is running, then kill it.

There're several ways to do so, for example suppose we have a timer that should execute the proc check:voices.

Code: Select all

timerexists check:voices
this code returns the timer id of the timer which should execute check:voices. So to catch it and kill it, we can use:

Code: Select all

if {[set thetimer [timerexists check:voices]]!=""} {
 # if it's == "" then it doesn't exist, otherwise it returns the timerID
 killtimer $thetimer
 # timer is killed
}
another way would be to use lsearch with the glob switch.

Code: Select all

if {[set timerindex [lsearch -glob [timers] *check:voices*]] != -1} {
 # if it's == -1 then it doesn't exist, otherwise it returns the index of this timer.
 killtimer [lindex [lindex [timers] $timerindex] 2]
 # the 3rd element of this list is the timerID which is now killed.
}
Now the previous 2 methods will not match if there's a difference in case (i.e. Upper or Lower letters.)
So this way will match whatever case is used.

Code: Select all

foreach thetimer [timers] {
 if {[string equal -nocase check:voices [lindex $thetimer 1]]} {
  # got a match
  killtimer [lindex $thetimer 2]
  # killed the timerID which is the 3rd element of $thetimer.
  break
  # stopped since no need for further checks.
 }
}
Same methods can be applied to utimers, just change timer(s) to utimer(s).
Last edited by Sir_Fz on Mon Jul 30, 2007 4:00 pm, edited 2 times in total.
User avatar
Sir_Fz
Revered One
Posts: 3793
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

You can also search for a timer independant from any cases (capital or lower letters) using lsearch.

Code: Select all

lsearch -glob [string tolower [timers]] "* {check:voices} *"
but note that check:voices should be in lowercase as well. so if you want to make sure that a variable is in lowercase use

Code: Select all

string tolower $variable
PS: After a test of which method is faster (lsearch or foreach and matching) I found out that using the foreach method is faster.
User avatar
sKy
Op
Posts: 194
Joined: Thu Apr 14, 2005 5:58 pm
Location: Germany

Post by sKy »

i might add:

Code: Select all

timer time command
returns timerID
example: set timer [timer 1 [list putserv "..."]]
same for utimer.

this timer-id can you put in a variable (perhaps a global variable) and use later.

some less important procs:

Code: Select all

proc isutimer { id } {
	foreach utimer [utimers] {
		set isid [lindex $utimer 2]
		if { $id == $isid } {
			return 1
		}
	}
	return 0
}

proc istimer { id } {
	foreach timer [timers] {
		set isid [lindex $timer 2]
		if { $id == $isid } {
			return 1
		}
	}
	return 0
}
Ex:
if { [istimer $timerid] } { killtimer $timerid }
User avatar
Sir_Fz
Revered One
Posts: 3793
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

Incase you have a timer which has more than 1 element in it and some of these elements always vary (i.e. $nick $uhost...etc) then you can use string match instead of string equal to be able to use wildcards. Suppose we have:
{12 {check:voices #bla} timer1}
We can use this code to find and kill this timer:

Code: Select all

foreach thetimer [timers] { 
 if {[string match -nocase "check:voices *" [lindex $thetimer 1]]} { 
  # got a match 
  killtimer [lindex $thetimer 2] 
  # killed the timerID which is the 3rd element of $thetimer. 
  break 
  # stopped since no need for further checks. 
 } 
}
And ofcourse you can also use "lsearch -glob"

Code: Select all

if {[set timerindex [lsearch -glob [timers] "* {check:voices *} *"]] != -1} { 
 # if it's == -1 then it doesn't exist, otherwise it returns the index of this timer. 
 killtimer [lindex [lindex [timers] $timerindex] 2] 
 # the 3rd element of this list is the timerID which is now killed. 
}
but the first method is faster.
User avatar
Sir_Fz
Revered One
Posts: 3793
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

Btw, [timerexists] and [utimerexists] are alltools.tcl commands. Incase you're not loading it:

Code: Select all

proc timerexists {command} {
  foreach i [timers] {
    if {![string compare $command [lindex $i 1]]} then {
      return [lindex $i 2]
    }
  }
  return
}

proc utimerexists {command} {
  foreach i [utimers] {
    if {![string compare $command [lindex $i 1]]} then {
      return [lindex $i 2]
    }
  }
  return
}
User avatar
awyeah
Revered One
Posts: 1580
Joined: Mon Apr 26, 2004 2:37 am
Location: Switzerland
Contact:

Post by awyeah »

Since we are dealing with timers here, some info regarding the minute, hour, day, month, year masks for bind time and how they are used for specific intervals would be appreciated.
·­awyeah·

==================================
Facebook: jawad@idsia.ch (Jay Dee)
PS: Guys, I don't accept script helps or requests personally anymore.
==================================
User avatar
Sir_Fz
Revered One
Posts: 3793
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

TIME (stackable)
bind time <flags> <mask> <proc>
proc-name <minute> <hour> <day> <month> <year>

Description: allows you to schedule procedure calls at certain
times. mask matches 5 space separated integers of the form:
"minute hour day month year". minute, hour, day, month have a
zero padding so they are exactly two characters long; year is
four characters. Flags are ignored.
What's also worth mentioning about the time bind is that the first day is 01 and the first month is 00 (this is not documented).

If you want to trigger an operation every x minutes without using the (u)timer command, then bind time is what you need. Example:

Code: Select all

bind time - * operation

# This proc (operation) is called every minute (00, 01, 02, 03,...59)
proc operation {min hour day mon year} {
 # We want to trigger our command every 3 minutes for example
 if {[scan $min %d] % 3 == 0} {
  # Perform the needed operation here
 }
}
[scan %d] is applied over $min in order to convert it from an octal to a decimal number (since 08 & 09 are invalid octals) to be divided over the number of minutes (the interval of your choice which is 3 in our example). If the remainder is not 0 (greater than 0) then $min is not divisible by 3 and thus 3 minutes did not pass. This means that the "needed operation" is triggered at minutes 00, 03, 06, 09, ..., 57 (all divisible by 3).
User avatar
Sir_Fz
Revered One
Posts: 3793
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

In case you are worried about minute intervals which are not divisible by 60, there is a solution. What I mean by "not divisible by 60" are intervals like 7 for example: 00, 07, 14, 21, 28, 35, 42, 49, 56 - now the operation should be performed at minute 03 but instead it triggers at minute 00 which is not after 7 minutes. To solve this, you can use:

Code: Select all

bind time - * operation

# This proc (operation) is called every minute (00, 01, 02, 03,...59)
proc operation {min hour day mon year} {
 # We want to trigger our command every 7 minutes
 if {([scan $min %d]+([scan $hour %d]*60)) % 7 == 0} {
  # Perform the needed operation here
 }
}
What's happening here is that the hour is being converted into its minutes equivalent, added to the minute, and then divided by the interval to check if the remainder is 0. This way the difference between every call of the needed operation is always 7 minutes.

This, of course, can be applied on all intervals (divisible or not divisible by 60) so I recommend using it as default.
User avatar
awyeah
Revered One
Posts: 1580
Joined: Mon Apr 26, 2004 2:37 am
Location: Switzerland
Contact:

Post by awyeah »

The mask is "MM HH dd mm yyyy" and can have wildcards (MM=minute, HH=hour, dd=day, mm=month(-1), yyyy=year)

The month comes with -1. So, january is 00 and december is 11.

Examples:

Code: Select all

bind time - * procname ; # is called every minute
bind time - "?5 *" procname ; # is called every 10 minutes, 05,15,25,35,45,55
bind time - "30 *" procname ; # is called every hour, 30 mins after full
bind time - "30 1? * 200?" procname ; # work from 10:30 - 19:30 every hour, but only in the year 2000-2009

Code: Select all

proc procname {minute hour day month year} { .. } ; # full var support
proc procname {min hour args} { .. } ; # args is special var for a list
proc procname args { .. } ; # you dont need the time, only proc called
Hint:

To trim the minutes (or other vars) it is best to use:

Code: Select all

set minutes [string trimleft $minutes 0]; if {$minutes==""} {set minutes 0}
Last edited by awyeah on Tue Jul 24, 2007 10:56 am, edited 1 time in total.
·­awyeah·

==================================
Facebook: jawad@idsia.ch (Jay Dee)
PS: Guys, I don't accept script helps or requests personally anymore.
==================================
User avatar
Sir_Fz
Revered One
Posts: 3793
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

awyeah wrote:Hint:

To trim the minutes (or other vars) best use:

Code: Select all

set minutes [string trimleft $minutes 0]; if {$minutes==""} {set minutes 0}
Or simply convert it to decimal as done in my code above:

Code: Select all

set minutes [scan $minutes %d]
Post Reply