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.

[SOLVED!] Unable to get procs to load/run (sounds newbie...

Help for those learning Tcl or writing their own scripts.
Post Reply
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

[SOLVED!] Unable to get procs to load/run (sounds newbie...

Post by dj-zath »

hi nml, arfer and the rest of the gang..

Zath here..

now I feel kinda "stupid" with this one... but I'll try to explain..

recently, I had to take my radio service down.. reasons include cost and drama/fights (to put it in short) so, I decided to "scale things down" for a little while..

I decided to try "starting over".. WAY over.. I got myself an account on Ustream.. yadda yadda yadda... but in the process I have lost all my BSD boxes and now I am down to just a couple of- ready for this??-- Windows boxes (I'm sure you can see where THIS is going...)

I'm trying to set up a "windrop" and load some simple TCL scripts in it..

the problem isn't the Windrop, but TCL.. it doesn't seem to work correctly in windows... yeah, I have all the libs and such that came with the silly Windrop thing.. and the bot DOES load.. but I can't get it to even read/exec a simple 'called' proc without bawking out some errors!

example:

Code: Select all


if {(![info exists DetRAC])} {
        set DetRAC "0"
        utimer 10 RunLoop1
        return 0
}

proc RunLoop1 {} {
       putlog "this is a test"
       utimer 10 RunLoop1
       return 0
}

(note: there were 2 typos, in this post, that were corrected that were not in the actual code)


and this returns errors and won't load or run..

error message is:
  • Tcl error in script for 'timer 1':
    invalid command name "RunLoop1"
orginally, when I tried to load my old config, it barfed out all kinds of errors (of course I expected it to, to some extent) but now I have "started over" fresh with a new conf and all.. and thats one of the very first procs...

it seems that it won't load ANY "called" procs at all and fails for procs called by binds as well...

so, I sit here bewildered.. feeling stupid!! :)

I should KNOW why this is.. but its just such a slap in the face... I'm just not thinking it right somewhere...

any ideas as to what I'm doing wrong?

thanks for reading this rant :)

-DjZ-
:) :)

UPDATE:

I just recalled I had this problem before.. and I have discussed it here in
THIS POST. the problems are almost-exactly the same- new install of TCL and everything BREAKS.. -sigh- I'm just having the "procs can't call other procs" issues and I can't call any procs with binds or other procs- with or without flags/delimiters.. and so far, I haven't come up with a working solution.. I didn't, the last time before, and I havent now. I guess I really don't understand this TCL stuff.. I hate TCL!

-DjZ-
Last edited by dj-zath on Wed Dec 15, 2010 9:18 am, edited 5 times in total.
User avatar
Trixar_za
Op
Posts: 143
Joined: Wed Nov 18, 2009 1:44 pm
Location: South Africa
Contact:

Post by Trixar_za »

Weird Questions:
1. Why do you have two spaces between proc and RunLoop1?
2. Why do you have an ; at the end of the proc's }?
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

hi and thanks for your reply

the code you see isn't "copy/pasted" so they are typos

this is because you can't copy/paste across different machines...

I had to flip back and forth from different consoles and hand-type the code and error message.

in response I will now edit the post and correct this before it confuses others

-DjZ-
:) :)
User avatar
Trixar_za
Op
Posts: 143
Joined: Wed Nov 18, 2009 1:44 pm
Location: South Africa
Contact:

Post by Trixar_za »

Why didn't you just create an example script file which you could copy your current machine so you could post the code directly?
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

trixar_za wrote:Why didn't you just create an example script file which you could copy your current machine so you could post the code directly?
Hi again and huh??

I'm sorry, I didn't quite understand that... isn't that what I have already did when I posted the snippet?

Let's see, if I can stay connected long enough, perhaps I can fire up a thumb drive and break out the ole sneaker net :)

..and, here we go:

Code: Select all

checkmodule		"blowfish"
loadmodule		"server"
loadmodule		"irc"
loadmodule		"channels"

set userfile		"Bot.user"
set chanfile		"Bot.chan"
set pidfile		"Bot.pid"

set use-ssl		"0"
set keep-nick		"1"
set die-on-sighup	"0"
set die-on-sigterm	"0"
set net-type		"5"
set quiet-save		"1"
set ctcp-mode		"1"
set opchars		"@&~"

set nick		"PlayBot"
set altnick		"PlayBot"
set username		"DJ_Piezo"
set realname		"Piezo's Place - Playout and Control Interface"

set my-ip		"(removed)"
set My-hostname		""

set MyChan		"Piezo"
set servers		"chat1.ustream.tv:6667"

if {(![info exists MyChan])||($MyChan == "")} {set MyChan "Test";}

channel add #$MyChan {
	chanmode	"+ntf-ikms 26:30"
	idle-kick	"120" 
	need-op		""
	need-invite	""
	need-key	""
	need-unban	""

	flood-chan	"25:30"
	flood-nick	"04:10"
	flood-join	"05:05"
}

unbind	msg	-	hello		*msg:hello
unbind	msg	-	ident		*msg:ident
unbind	msg	-	addhost		*msg:addhost

bind	evnt	-	init-server	BotLogin
bind	join	-	*		Greet


#### EVENTS ####

set	init-server {
	set nick "DJ-Piezo"
	set altnick "DJ-Piezo"
	putserv "MODE $botnick +Bi-ws"
}

proc	BotLogin {type} {
	putserv "NICK DJ-Piezo:(removed)"
}

proc	Greet {nick args} {
	global botnick MyChan
	if {($nick != $botnick)} {
		putserv "PRIVMSG #$MyChan :Welcome To Piezo's Place!"
		putserv "PRIVMSG #$MyChan :type /nick name to give yourself a name (32 chars max)"
		putserv "PRIVMSG #$MyChan :example: /nick JohnnyBoy"
	}
}

proc	BindComm {} {
	global MyChan

	bind	pub	-	!song		ListSong

	bind	pub	-	!search		SrchAR
	bind	pub	-	!sch		SrchAR
	bind	pub	-	!request	RqstAR
	bind	pub	-	!req		RqstAR

	bind	pub	-n	!stop		OtsStop
	bind	pub	-n	!play		OtsPlay
	bind	pub	-n	!prev		OtsPrev
	bind	pub	-n	!next		OtsNext

	bind	pub	-n	!auto-on	AutoON
	bind	pub	-n	!auto-off	AutoOFF
	bind	pub	-n	!sch-on		SrchON
	bind	pub	-n	!sch-off	SrchOFF

	putserv "PRIVMSG #$MyChan :Commands Now Enabled!"
	return "0"
}


#### RUN LOOPS ####

if	{(![info exists DetRAC])} {
	set DetRAC "0"
	utimer 30 [list RunLoop1]
	utimer 32 [list RunLoop2]
	utimer 35 [list BindComm]
	return "0"
}

proc	RunLoop1 {} {
	GetInfo
	utimer 3 [list RunLoop1]
	return "0"
}

proc	RunLoop2 {} {
	Output
	utimer 3 [list RunLoop2]
	return "0"
}


#### TOGGLES ####

proc	AutoON {} {
	return "0"
}

proc	AutoOFF {} {
	return "0"
}

proc	SrchON {} {
	return "0"
}

proc	SrchOFF {} {
	return "0"
}


#### RAC COMMANDS ####

proc	OtsStop {} {
	return "0"
}

proc	OtsPlay {} {
	return "0"
}

proc	OtsPrev {} {
	return "0"
}

proc	OtsNext {} {
	return "0"
}


#### CALL PROCS ####

proc	ListSong {} {
	global MyChan
	return "0"
}

proc	SrchAR {} {
	return "0"
}

proc	RqstAR {} {
	return "0"
}


#### RUN PROCS ####

proc	GetInfo {} {
	return "0"
}

proc	Output {} {
	return "0"
}
there ya go!

the procs are "empty" because thats as far as I got.. like it said i was starting over from scratch! :)

granted the procs in this [current] example don't do ANYthing, however, they should load without any errors but they ALL error except BindComm, Greet and BotLogin.

I will also add that all this WORKED in the orginal code when running the bot in FBSD on a different box and, although, I'm expecting to have problems with SOCKETS on this windows box, I didn't expect to be stopped at the gate and get stuck with, what probably is either a FUBARed version of TCL (v8.5) or some change that now requires something I know nothing about... The "orginal" code also failed miserably- as I expected it to.. but for different reasons, I cannot use it.. it was written to interface an Icecast server and playout system remotely using an external socket handler.. it also fails in the same way, however, where all the "called" procs error with "invalid command" messages when installed on this windows box but- I didn't expect the orginal code to work at all, anyways, which is why I started over.. I can eliminate 2/3 of it (2200 lines in total) and just interface the playout box locally and no need to parse any Icecast servers or employ automatic DJ detection and switching...

And now, I'm just falling even further and further behind as I become more and more flustered... If just getting procs to even work in this machine is going to be difficult, I can't imagine whats in store for me once I start calling up sockets! (I won't be able to use any external socket handlers here) I'm going to have to learn to use the http module.. which, last I learned, can break on the TTL setting.. or the orginal sock-get command which will hang the bot if the socket is "less than immediately-responsive" but thats "down the line"; right now, I need to get some procs to load... damn NEWBIE stuff.. and its FAILING MISERABLY!

Really, all I needed was a working example.. however, nothing I have tried so far is working.. nothing at all!

-DjZ-
:( :(
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

DING!

After an extensive google search, I found the solution...

It turns out that the Windrop uses the TK version of TCL which handles "return" lines differently from the version of TCL that comes with FBSD.. (actually TK FIXED it)

Omitting the return lines in the procs and loops solved the problem; now the loops are running.. and I can now continue... YEY!

Damn! why did they have to go and re-invent the wheel again? I'll have to re-learn TCL all over.. AGAIN!

UPDATE:

in closer inspection:

I have discovered I have actually made an error in my code, that went un-noticed, but the TK version of TCL DID find it!

let's just say I'm an IDIOT LOL

I placed a "return" in the "loop trigger" which sits in the PRIMARY proc run... (one should NEVER place returns in the primary proc) therefore, the return was CANCELLING OUT everythig past it!

..and that has never been noticed before! its noticed NOW, however.. and makes PERFECT sense... its working EXACTLY as it SHOULD.. the return was omitted.. and now its behaving as expected...

I thought I should come back and mention this- incase it happens to someone else... :)

see? I TRY hard to learn and understand what I am doing.. and why things break!

it really was a "newbie" mistake!

"Crow still tastes like **** even with ketchup!" - DJ Zath

-DjZ-
:) :)
User avatar
Trixar_za
Op
Posts: 143
Joined: Wed Nov 18, 2009 1:44 pm
Location: South Africa
Contact:

Post by Trixar_za »

I meant create a tcl file which could be transferred between machines - it would be way easier than having to retype the whole thing on a forum :P
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

@dj-zath:
tk is an extension to tcl, not a different version of tcl. Thus, your eggdrop is still linked against a tcl-library. Even further, there's no command named "return" in tk; issuing a return-command in a tk-application will run the same return command as a (non-tk) tcl-let would...

Now, the error you initially posted would rather suggest that your code was not properly structured; missing }, quote (") and such. This would then either stop the parsing of the script before the needed proc was defined, or "skip past it", while still starting the timer

Why would things start to appear when you switched from *bsd to windows? My guess would be improperly structured code combined with different newline-character

Return in a "primary proc"? Errr, that elevates the question: "what is a primary proc"?
You should always use return when you'd like a proc to terminate execution before it reaches it's end, and possibly at the end of the proc if you'd like to return a different value than the return-value of the last issued command.
NML_375
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

Hi nml buddy!

first off, thanks for the post/reply..

now, to explain things a little.. since it seems that I have confused some ..

The post you are referring to where "tcl.tk" was referred was actually posted in parts.. I have made a claim based on something I read on a page in Google.. I have misunderstood the concept they were explaining completely but the funny part is, I actually DID see the solution to my problem in that strange explanation that I was reading..

upon later, returning to that page, (and this post) I have realized they were actually talking about using catch to catch errors.. though in their examples, I saw the "flaw" and then took my return string out of the loop command.. and DING it WORKED..

but I wanted to know WHY it didn't work.. then I, again, saw why it failed.. "main proc" or the master loop that the config file/settings itself exists.. let me see if I can explain myself since I lack the "proper term" here..

eggdrop ->TCL interepter -> [config level]-> procs > more procs-> etc etc

placing a "return" in a "sub level" proc is OKay.. but the timer TRIGGER was at the "config level" (as high as you can go in the config file where all the "core" settings sit) I assume, to eggdrop/TCL, that the whole cfg file itself is a single-ended proc all by itself..


in eggdrop:

Code: Select all

proc  config{whatever flags go here} {
        source eggdrop.conf
}
well I placed the "return" at the "root" level of the config file itself.. so everythig PAST it was "ignored/dropped (never even loaded)"!

it was a newbie mistake!

I understand what I did and corrected it.. and then anticipated "socket issues".. and, though to my surprise, using standard TCL sockets actually WORKED better than I initially expected..

heres a snippet of what I was working on.. this is WORKING code...

Code: Select all

if {([catch {set Sock [socket 127.0.0.1 1234]; fconfigure $Sock -blocking on; puts $Sock "GET /x/playing.cgi HTTP/1.0\r\nHost: 127.0.0.1:1234\r\nConnection: close\r\n\r\n\r\n"; flush $Sock; while {(![eof $Sock])} {set VarA [read $Sock]}; close $Sock;}])} {
    incr RacErr; 
    if {($RacErr == "4")} {
        set DetOT "0"; 
        set RacInfo ""; 
    } elseif {($RacErr > "4")} {
        set RacErr "5"; 
    }; 
} else {
    set RacErr "0"; 
    set DetOT "1f"; 
    set VarA [string first "\<BODY bgcolor\=" $RacInfo]; 
    set VarA [string range $RacInfo $VarA [string length $RacInfo]]; 
    set VarB [string first "\<\/BODY\>" $VarA]; 
    set VarB [expr $VarB - "1"]; 
    set RacInfo [string range $VarA "193" $VarB]; 
}; 
This actually WORKS, however, in some instances it seems to "miss" occasionally.. which is odd in itself.. What I mean by "miss" is, although the socket is detected, there won't be any return.. and this happens intermittantly and only on one or 2 instances (this is a base code to how allthe sockets are read throughout the code) though some of these are intermittant, other places run flawlessly...

so far, thats all I've experienced having to rewrite it all.. as I explained eariler, the reason is the bot won't be managing any DJs and simuilar.. I just needed a simply "song announcer" but it has to be the playout system (not winamp) and it has to be reliable.. I'm not sayng I need help- I was just giving you "heads up" in case you were curious...

-DjZ-
:) :)
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

placing a "return" in a "sub level" proc is OKay.. but the timer TRIGGER was at the "config level" (as high as you can go in the config file where all the "core" settings sit) I assume, to eggdrop/TCL, that the whole cfg file itself is a single-ended proc all by itself..
Actually, the config-file is a normal tcl-script, which is executed at the global level. No procs involved there. And yes, issuing a return command when evaulating a script will interrupt the parsing of the script at that point, without raising an error condition.

Most common reason for unexpected returns is, as I mentioned earlier, mis-aligned or missing brackets, quotes, etc.

Now, for the socket code.. Please, please, please indent your code in a manner that it actually gets readable...
I took the liberty of tidying it up this time, which makes the problem rather obvious:

Code: Select all

if {
  [catch {
    set Sock [socket 127.0.0.1 1234]
    fconfigure $Sock -blocking on
    puts $Sock "GET /x/playing.cgi HTTP/1.0\r\nHost: 127.0.0.1:1234\r\nConnection: close\r\n\r\n\r\n"
    flush $Sock
    while {![eof $Sock]} {
      set VarA [read $Sock]
    }
    close $Sock
  }]
} {
  incr RacErr
  if {$RacErr == 4} {
    set DetOT 0
    set RacInfo ""
  } elseif {$RacErr > 4} {
    set RacErr 5
  }
} else {
  set RacErr 0
  set DetOT 1f
  set VarA [string first "\<BODY bgcolor\=" $RacInfo]
  set VarA [string range $RacInfo $VarA [string length $RacInfo]]
  set VarB [string first "\<\/BODY\>" $VarA]
  set VarB [expr $VarB - 1]
  set RacInfo [string range $VarA 193 $VarB]
}
The issue is with your while-loop, which will continue to call read on the connected socket, until there is an eof-condition (since you use blocking connections, that would be when the remote server drops the connection). For each time that you call read, you replace the previous value of VarA with whatever you just managed to read. Depending on system and network speed, each read will consume one or more ethernet frames, and it is very likely that you'll have to call read multiple times to catch all frames and get an eof condition.
... And hence you randomly seem to loose the content.

How to solve this? Push the read data to a buffer, and parse the whole buffer.
I would also strongly (once again) suggest that you use asynchronous operations using the fileevent mechanism..
NML_375
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

good ole nml! thanks for the reply! :-)

as per your suggestion, I agree; that I should use async sockets.. and, asside of a few places in the code, I have!

actually I used async sockets everywhere that I didn't need a "return" from the socket (example: to send a command string which doesn't need a return).

but just in 4 places do I need to get return data...

I have discovered one of my problems.. and thats that there are SPACES in the data strings that are being sent... which explains the intermit nature.. if the title string, for example, contains only one word, then it works fine.. add 2 words or more... and strange things start happening..

example to this is: where I have to send a search string of the currently-playing song.. back to the playout system to retrieve its index number (yeah, it is VERY STUPID that the playout system itself doesn't send the index number with the playing song) while search and play queries NEED said number to add them to the playout Queue.. its silly! So, I take the TITLE of said playing track and issue a search back on itself to get its index number.. and I was getting back WRONG or odd index numbers!

the light came on when I sent a title "poker face" and got the index number to the NEXT track in the library of almost the same name.. "poker" was what was being sent.. as the rest of the string truncated (or BROKE) at the first SPACE!

I didn't experience this before when using "fetch" as a socket-handler, so I didn't expect it.. but I'm aware of it now and added a string map to change spaces to +'s.. which fixed the problem half-way... I'm goint to try using async sockets again, once I get to a point where I can readily-read the data without having to write a whole bunch of "debug" code just for that test...

but that piece of code above also solved some of the intermittant issues as well- since it has a "built in buffer" as you pointed out.. $Sock is pushed into $RacInfo and thats where all the parsing takes place.. $RacInfo is also "cached" so if the socket returns nothing, it will hold the last-retrieved data for 5 cycles at which point it will either release the buffer (clear $RacInfo) or get new data from $Sock.. this happens at anytime for each read.. so it greatly-reduces "flaky" data reads from funky sockets (such as when I was DSL and trying to read a broken net connection) which is why I didn't see [the intermit] on some of the sockets; all these sockets are also localhost- on the same machine... I didn't think it too much of a problem.. but, I do like to be proper and do it right..

async sockets are the plan... (if I can get them to work, that is) I still have all the examples we worked on before and will be reviewing them again (I understand a lot more since the last time I looked at those examples) So I'm sure I'll get this all up and running in little to no time...

again, thanks for the reply!

-DjZ-
:) :)
Post Reply