The CGI script edademo.cgi

This is the most complex piece of the demo. It is also the least necessary, given that we have already covered all pieces necessary to efficietly completing a FlowTracer based design using the CLI and the GUI.

Let's start from the main part of the code, which looks quite similar to the CGI scripts that we have already seen in the CGI tutorial. In bold, you can notice the parsing of QUERY_STRING, which is needed to determine which page to display, based on the values of the "user" and "action" variables.

edademo.cgi: Main code
#!/bin/csh -f
# The rest is -*- Tcl -*- \
exec vovsh -t $0 $*

## Definition of all procedures

################################# MAIN CODE 

VOVHTML_START 

set listOfUnits {}
set listOfSteps { synth place route sta drc lvs }
set listOfUsers {}
set results()   "Initialization of results array" 

set opt(reload) 0
set opt(user)   ""
set opt(mode)   ""
set opt(action) "summary"

foreach option [split $env(QUERY_STRING) &] {
    if [regexp {(.*)=(.*)} $option all var value] {
        set var [string tolower $var]
        set opt($var) [url_decode $value]
    }
}

switch $opt(action) {
    "job"    { 
        showJobReport  $opt(id)    
    }
    "user"   {
        getResultsJobs   $opt(user)
        getResultsArea   $opt(user)
        getResultsTiming $opt(user)
        showUserReport $opt(user) $opt(mode) $opt(reload)
    } 
    default {
        showProjectSummary  $opt(reload)
    }
}

VOVHTML_FINISH

getResultsArea

In the procedure getResultsArea we search the trace database for files that contain area information, which in our case are files with a suffix area.rpt. We create a temporary set of all such files (with vtk_set_create), and then we get all elements in the set (with vtk_set_get_elements). We look inside each of the files (with source $namex) and store the area information in the results array, taking care of flagging whether the data up-to-date or not, by looking at the status (@STATUS@) of the report file. Finally, we cleanup by forgetting the temporary set.

edademo.cgi: collect data from report files:
proc getResultsArea { userToReport } {
    global results
    set setName "@@@:tmp:grarp[pid]" 
    set setRule "isfile name~/$userToReport/.*area.rpt$"
    set setId   [vtk_set_create $setName $setRule]
    foreach fileInfo [vtk_set_get_elements $setId "@STATUS@ @NAME@"] {
        set status [shift fileInfo]
        set name   [shift fileInfo]
        set namex  [vtk_path_expand $name]
        if [file exists $namex] {
            set unit [file root [file root [file tail  $namex]]]
            catch {source $namex}
            set results($unit,area,status) $status
        }
    }
    vtk_set_forget $setId
}

getResultsJobs

In the procedure getResultsJobs we query the trace database for information about the status, duration, etc. about each of the CDT jobs in the flow. We store the information in the results array.

First we get a list of all the sets in the trace (with vtk_trace_list_sets). We are interested only in the sets with name beginning with "CDT:". We get the elements of each set (with vtk_set_find and vtk_set_get_elements).

edademo.cgi: collect data from trace
proc getResultsJobs { userToReport } {
    global results
    global listOfUnits
    global listOfUsers

    foreach setName [vtk_trace_list_sets] {
    # puts '$setName'
    if [regexp {^CDT:(.*):unit:([^:]+)$} $setName all user unit] {
        lappend_no_dup listOfUnits $unit
        lappend_no_dup listOfUsers $user

        if { $user != $userToReport } continue

        set setId [vtk_set_find $setName]
        set results($unit,setId) $setId
        foreach jobInfo [vtk_set_get_elements $setId "@ID@ @STATUS@ @COMMAND@"] {
            set id   [shift jobInfo]
            set st   [shift jobInfo]
            set tool [lindex $jobInfo 1]

            if  {  $tool  == "cdt" } {
                set step [lindex $jobInfo 2]
                set results($unit,$step,id) $id
                set results($unit,$step,st) $st
            }
        }
    }
    if [regexp {^CDT:(.*):step:([^:]+)$} $setName all user step] {
        if { $user != $userToReport } continue
            set setId [vtk_set_find $setName]
            set results(step,$step,setId) $setId
        }
    }

    set listOfUnits [lsort $listOfUnits]
    set listOfUsers [lsort $listOfUsers]
}

Page Layout

To give all pages a similar look and functionality, we wrote procedure EDADEMOPAGE, which takes the following arguments:
  • title, which is the title of the page
  • reloadMs, the value in milliseconds of the auto-reload period
  • menu_script, a script to customize the menu on the left hand side of the page
  • body_script, a script with the actual content of the page

The scripts are evaluated using the Tcl command uplevel.

edademo.cgi: The page layout
proc EDADEMOPAGE { title reloadMs menu_script body_script } {
    global env
    HTML {
        HEAD { omitted }
        BODY {
            TABLE CELLSPACING=0 CELLPADDING=6 BORDER=0 WIDTH="100%"  {
                TR BGCOLOR="white" {
                    omitted title code
                }
                TR {
                    TD BGCOLOR="$bgColor" VALIGN=TOP {
                        HREF "/cgi/edademo.cgi" "Summary"; BR
                        if { $menu_script != {} } {
                            uplevel $menu_script
                        }
                    }

                    TD VALIGN=TOP COLSPAN=2 BGCOLOR="#BBCCBB" {
                        uplevel $body_script
                    }
                }
            }
        }
    }
}

Following is a collection of procedure used to render the data in HTML. Note the use of vtk_time_pp to print durations in a human readable form and of the global array vov_color() to colorize the cells in the tables according to the status of the node they represent.

edademo.cgi: Data rendering
proc showStepInfo { unit step displayMode } {
    global results
    global vov_color

    set st [getResult $unit,$step,st EMPTY]
    if { $st == "EMPTY" } {
        TD {} 
        TD {}
        TD {}
        return 
    }
    set du [getResult $unit,$step,du n/a]
    set id [getResult $unit,$step,id 0 ]
    set ag [getResult $unit,$step,ag 0 ]

    set url  "/cgi/edademo.cgi?action=job&id=$id"
    if { $du >= 0 } {
        set tim  [vtk_time_pp $du] 
    } else {
        set tim  [vtk_time_pp $ag]
    }

    if { $displayMode == "simple" } {
        TD ALIGN="right" COLSPAN="2"  { SMALL { HREF $url $tim   }  }
        TD BGCOLOR=$vov_color($st) { OUT "  " }
    } else {
        TD ALIGN="right"  { SMALL { HREF $url $tim   }  }
        TD ALIGN="center" { SMALL { HREF $url [string tolower $st]   }  }
        TD BGCOLOR=$vov_color($st) { OUT "  " }
    }
}

proc showResults  { user { displayMode "simple"} } {
    global results
    global vov_color
    global listOfUnits
    global listOfSteps

    TABLE BORDER="0" CELLSPACING="2"  CELLPADDING="5" {
        TR BGCOLOR="white" {
            TH COLSPAN=2 { OUT "Unit" }
            TH { OUT "Area"  }
            TH { OUT "Slack" }
            foreach step $listOfSteps {
                set setId [getResult step,$step,setId 0]
                TH COLSPAN=3 { 
                    if { $setId == 0 } {
                        OUT "$step"
                    } else {
                        HREF "/set?id=$setId&action=showgrid" $step
                        if { $displayMode != "simple" } {
                            BR
                            SMALL {
                                omitted: some links
                            }
                        }
                    }
                } 
            }
        }

        set count 0
        foreach unit $listOfUnits {
            set setName "CDT:$user:unit:$unit" 
            set setId   [vtk_set_find $setName] 
            if { $setId != 0 } {
                set area    [getResult $unit,area        "n/a"] 
                set areaSt  [getResult $unit,area,status "INVALID"] 
                set slack   [getResult $unit,slack ""] 
                set slackSt [getResult $unit,slack,status "INVALID"]
                TR {
                    TD ALIGN="RIGHT" {  OUT [incr count] }
                    TH ALIGN="LEFT" { HREF "/set?id=$setId" $unit }
                    TD ALIGN=RIGHT BGCOLOR=$vov_color($areaSt) { 
                        COLOR $vov_color($areaSt,fg) $area 
                    }
                    TH ALIGN=RIGHT BGCOLOR=$vov_color($slackSt) { 
                        if { $slack != "" } { 
                            if { $slack %lt; 0 } {
                                COLOR "#FFFF88" "($slack)"
                            } else {
                                COLOR $vov_color($slackSt,fg) $slack
                            }
                        }
                    }
                    foreach step $listOfSteps {
                        showStepInfo $unit $step $displayMode
                    }
                }
            }
        }
    }       
}

proc showUserSummary { user setId }  {
    global vov_color

    if { $setId == 0 } {
        set status    EMPTY
        set nodes     0
        set places    0
    } else {
        set setInfo [vtk_set_statistics $setId]
        set saveSetInfo $setInfo
        set status     [shift setInfo]
        set nodes      [shift setInfo]
        set places     [shift setInfo]
        set jobs       [shift setInfo]
        set duration   [shift setInfo]
        set unknown    [shift setInfo]
        set placeStats [shift setInfo]
        set jobStats   [shift setInfo]

        set statusList { INVALID RUNNING VALID FAILED } 
        foreach s $statusList { set js($s) 0 }
        foreach { s n } $jobStats {
            set js($s) $n
        }

        TR {
            TH { print user name   }
            TD ALIGN="RIGHT" { OUT [vtk_time_pp $duration] }
            TD ALIGN="RIGHT" { OUT $jobs }
            foreach s $statusList {
                omitted: show colored ball
            }
        }
    }
}

showProjectSummary

The showProjectSummary procedure displays the summary page. First we get the list of users of the project (really the list of workspaces) by looking at the sets with name beginning with "CDT:", and then we call showUserSummary on each.

edademo.cgi: The project summary page
proc showProjectSummary { reloadMs } {
    set listOfUsers {}

    foreach setName [vtk_trace_list_sets] {
        if [regexp {CDT:(.*):step} $setName all user] {
            lappend_no_dup listOfUsers $user
        }
    }
    set listOfUsers [lsort $listOfUsers]

    EDADEMOPAGE "Project Summary  $env(PROJECT)" $reloadMs {
        omitted
    } {
        TABLE WIDTH="100%" border=0 CELLPADDING=10 {
            TR BGCOLOR="#DDDDFF" {
                foreach head {
                    Workspace Duration Jobs Invalid
                    Running Valid Failed Actions 
                } {
                    TH { OUT $head }
                }
                foreach user $listOfUsers {
                    showUserSummary $user [vtk_set_find "CDT:$user"]
                }
            }
            TR BGCOLOR="#DDDDFF" {
                TD COLSPAN=9 { OUT "Totals" }
            }

            showUserSummary ""  [vtk_set_find "System:nodes"]
        }
    }
}

showUserReport

The procedure showUserReport generates the page showing the jobs in a user workspace. Notice the use of EDADEMOPAGE and showResults, which have been explained above.

edademo.cgi: The user workspace report
proc showUserReport { user mode reloadMs } {
    global argv

    EDADEMOPAGE "EDA-Demo Report Workspace $user" $reloadMs {
        if { $mode != "hlf" && $mode != "llf" } { 
            omitted: autoreload control
        }
        showMenu $user $mode
    }  {
        switch $mode {
            "llf"   { showLowLevelFlow "$user"     }
            default { showResults $use      }
        }
    } 
}