How can I create an RFC 3339 timestamp in TCL? - tcl

I inherited a TCL script (I have zero familiarity with the language) and need to add an RFC 3339 timestamp to it:
2012-04-05T12:13:32.123456-08:00
After searching Google, I haven't found any means of displaying the microseconds or the timezone offset (I have found a way to show the timezone name, but that doesn't help).
Is there a way to do this without calling an external process?

In TCL8.5, you can try the following command:
% clock format [clock seconds] -format "%Y-%m-%dT%T%z"
2012-04-05T16:06:07-0500
That gives you everything except the sub-second resolution. The clock microseconds command will give you the time in microseconds, but I can't find a format string identifier that matches it. You can use this to build your own command from scratch:
proc timestamp_rfc3339 {} {
set us [clock microseconds]
set sec [expr {$us / 1000000}]
set micro [expr {$us % 1000000}]
set ts [clock format $sec -format "%Y-%m-%dT%T"]
regexp {(...)(..)} [clock format $sec -format "%z"] matched tzh tzm
return [format "%s.%06s%s:%s" $ts $micro $tzh $tzm]
}
Running this results in a timestamp like 2012-04-05T16:35:06.366378-05:00.
Edit: Updated code sample to incorporate user1179884's tweaks (see comments) and to wrap in a proc.

Related

Display permanently current time in Tcl

I have three questions, all of them closely related to each other. The most important one is the first one.
First: I am trying to display the current time in the format hours:minutes:seconds, so
set systemTime [clock seconds]
puts "The time is: [clock format $systemTime -format %H:%M:%S]"
But the above clock should be permanently updated, that is, the seconds-part of the clock should be running all the time.
Second: In the next step I would like to display milliseconds and they should be updated as well.
Third: I would like to execute a procedure at a certain point of time. More precisely: At a certain time, say 16:20 (the format here is hours:minutes), tcl musst execute a procedure, say proc SumUpInt, which I defined. It may be possible that I want to consider seconds and milliseconds as well when executing the proc.
I do not know how to do this. I have found many similar questions on some web sites, also on stack overflow, but I was not able to adapt some of these solutions to my problem.
Any help is welcome!
Thank you in advance.
There doesn't seem to be an output directive in clock format for milliseconds, so perhaps:
proc timestamp {} {
set t [clock milliseconds]
set systemTime [expr {int($t / 1000)}]
set milliSeconds [expr {$t % 1000}]
return [format "%s.%03d" [clock format $systemTime -format %T] $milliSeconds]
}
timestamp ;# => 14:41:13.032
You can turn this "realtime" with
proc run_timestamp {} {
puts -nonewline "\r[timestamp] "
flush stdout
after 100 run_timestamp
}
run_timestamp
vwait forever
But the vwait means this will block the Tcl interpreter. I don't have any thoughts right now about how to integrate this into your terminal.

How do I perform a command substitution in tclsh?

I want to capture the stdout of a command in a variable, similarly to command substitution in Bash:
#!/bin/bash
x="$(date)"
echo $x
I tried doing the same in tclsh but it doesn't do what I want:
#!/bin/tclsh
set x [date]
echo $x
If I execute the script withtclsh myscript.tclsh it gives an error:
invalid command name "date"
while executing
"date "
invoked from within
"set x [ date ]"
On the other hand, if I open a TCL interactive shell with tclsh, it does not give an error and the echo line prints an empty string.
Why is my program giving different results when I execute the script with or without the REPL? And it there a way to capture the output of a shell command and store it in a variable, similarly to command substitution in Bash?
When not using Tcl interactively, you need to explicitly use the exec command to run a subprocess.
set x [exec date]
# Tcl uses puts instead of echo
puts $x
In interactive use, the unknown command handler guesses that that's what you wanted. In some cases. Be explicit in your scripts, please!
You should probably replace running a date subprocess with the appropriate calls to the built-in clock command:
# Get the timestamp in seconds-from-the-epoch
set now [clock seconds]
# Convert it to human-readable form
set x [clock format $now -format "%a %d %b %Y %H:%M:%S %Z"]
(That almost exactly matches the output of date on this system. The spacing isn't quite the same, but that doesn't matter for a lot of uses.)

Create a new file every time a TCL script runs

I am new to TCL and got some stuff I need to automate and I need my code to log all the commands and results after the login process.
My main issue is that I need to create a distinct log file everytime I run the script and one way I found out was to "append" the unique "timestamp" to the file name.
Here is where it starts to get picky, you see, every time I try to append the variable "$time" to the filename it returns:
couldn't open "15-10-28/11:57:10--xxx.xxxx.xxxx.txt": no such file or directory
while executing
"log_file "$newfile" "
(file "ssh-test.tcl" line 31)
My code is as follows:
set user [lrange $argv 0 0]
set password [lrange $argv 1 1]
set ipaddr [lrange $argv 2 2]
set arg1 [lrange $argv 3 3]
set systemTime [clock seconds]
set time [clock format $systemTime -format %y-%m-%d/%H:%M:%S--]
set a "ssh"
set suffix ".txt"
append newfile "${a}${arg1}${suffix}"
set timeout -1
# now connect to remote UNIX box (ipaddr) with given script to execute
spawn ssh $user#$ipaddr
match_max 100000
# Look for passwod prompt
expect "*?assword:*"
# Send password aka $password
send -- "$password\r"
log_file "$newfile" ;
expect "*#"
send -- "\r"
send_user "This is the $argv0 Script"
send -- "scm $arg1\r"
expect "*#"
send -- "exit\r"
expect eof
If I use the 'set filename "${a}${arg1}${suffix}"' string and 'log_file "$filename"' it works just fine but it will append the new info to the already existing file and I want a new file everytime I run the script.
If I use the 'append newfile "${a}${arg1}${suffix}"' string and 'log_file "$newfile"' it won't work and return the error already referred.
Hope you guys can help me out and thanks in advance for any support.
You are creating the timestamp with / in it.
set time [clock format $systemTime -format %y-%m-%d/%H:%M:%S--]
While appending this to the variable newfile, it will become 15-10-28/11:57:10--xxx.xxxx.xxxx.txt.
Expect will think that there is a folder called 15-10-28 available and under which I have to create the file 11:57:10--xxx.xxxx.xxxx.txt. Since that folder is not available, you are getting the error message as no such file or directory
After figuring out that the date format was messing up my code, I started playing around with the special characters and got it working like this:
set time [clock format $systemTime -format %a_%b_%d_%Y#%H'%M'%S]
It is not the desired format but at least I got it working as I intended.

Can a VHDL configuration have generics of it's own?

What I want to do:
I want to pass the current date and time to a VHDL testbench so I can create nicer report file names.
The Problem:
The Top level VHDL file that is being called from my simulation TCL scripts is a configuration and I don't know how to pass the genericto it. The interesing part of the TCL script is the following:
set reportfilename "report_$testcase_id"
append reportfilename [clock format [clock seconds] -format _%Y-%m-%d_%H:%M]
vsim -t 10fs -gG_TC_REPORTFILE=$reportfilename -novopt work.CFG_TB_TOP_tc0027 - wlf result_$testcase_id.wlf
The resulting VSIM call looks like this:
# vsim -t 10fs -wlf result_tc0027.wlf -novopt -gG_TC_REPORTFILE=report_tc0027_2014-03-05_13:22 work.CFG_TB_TOP_tc0027
The problem is that G_TC_REPORTFILE is not a generic of CFG_TB_TOP_tc0027.vhd, but of one of the modules configured IN CFG_TB_TOP_tc0027.vhd
The relevant part of CFG_TB_TOP_tc0027.vhd:
configuration CFG_TB_TOP_tc0027 of TB_TOP_CFG is
for testcaseexecution
for TB_TOP_E_INST : TB_TOP_E
-------------------------------------------------------------
-- Testbench configuration
-------------------------------------------------------------
use entity work.TB_TOP_E(TB_TOP_sim)
generic map (
G_TC_STIMULUSFILE => "./testcases/tc0027.txt", --
G_TC_REPORTFILE => "./results/report_tc0027.txt",
G_TB_VNR => "V_03.10",
G_TC_NR => 27); --
for TB_TOP_A_sim
How would I pass the value from the TCL file to the generic G_TC_REPORTFILE of the TB_TOP_E entity? Can I somehow add a generic to the CFG file, or can I somehow specify for which entity the generic in the TCL file is meant? Best-case-scenario would be if I would not have to edit the vhdl files, only the TCL scripts.
The simulator is ModelSim SE 10.0b, I'm using VHDL 2008.
I have just found the answer myself:
You can indeed specify a path to the generic you want to overwrite. I changed the TCL file like this:
set reportfilename "./results/report_$testcase_id"
append reportfilename [clock format [clock seconds] -format _%Y-%m-%d_%H-%M.txt]
vsim -t 10fs -GTB_TOP_CFG/TB_TOP_E_INST/G_TC_REPORTFILE=$reportfilename -novopt work.CFG_TB_TOP_tc0027 -wlf result_$testcase_id.wlf
Which produces the following VSIM call:
# vsim -t 10fs -wlf result_tc0027.wlf -novopt -GTB_TOP_CFG/TB_TOP_E_INST/G_TC_REPORTFILE=./results/report_tc0027_2014-03-05_14-06.txt work.CFG_TB_TOP_tc0027
In addition to adding the path TB_TOP_CFG/TP_TOP_E_INST/ I also had to remove the : from the timestamp, as modelsim/vhdl did not handle it.

How can I modify this eggdrop TCL script to work a little differently

I'm trying to set up a simple eggdrop bot to give a countdown til a certain event. The idea is that I give it a predetermined date and time, the user types !countdown and the bot replies with "There are x days, x hours, x minutes until it happens". This is the script as I found it (changed only to add the event's unixtime date in place of what it had), and running it on my eggdrop bot gives a response, but of course it isn't the response I needed (the important thing is that it worked at all).
I don't think there's a big difference between what it does and what I want it to do, but I have no idea how to correctly modify it. So I wondered if anyone here could show me how to do what I'm trying to do.
bind pub - !test countdown
proc countdown { nickname hostname handle channel arg } {
set date1 "1385798400"
# finds the time and date now
set now [unixtime]
# counts the time passed scince now
incr now -$date1
# shows how long has passed since $date1
set then [duration $now]
puthelp "PRIVMSG $channel :date1 was: $then ago, $nickname | \([\
clock format $date1 -format %d.%m.%Y] # [\
clock format $date1 -format %H:%M:%S]\)"
}
The hard part of working with dates is parsing and formatting, but you've already got utility commands to do that (duration and clock format). To work out the time remaining to an event in the future, you've just got to make sure that you do timestampfuture event - timestampnow in your calculations.
proc countdown {nickname hostname handle channel arg} {
set date1 "1385798400"
# finds the time and date now
set now [unixtime]
set left [duration [expr {$date1 - $now}]]
# Easier to store complex stuff in a variable and substitute stuff in
set formatted [clock format $date1 -format "(%d.%m.%Y # %H:%M:%S"}]
puthelp "PRIVMSG $channel :date1 will be: $left in the future, $nickname | $formatted"
}
Just bind that and off you go.