Changeset 6473


Ignore:
Timestamp:
Aug 7, 2016 6:08:40 PM (8 years ago)
Author:
ldelgass
Message:

Merge simsets, result cache (instant-on) from release branches.

Location:
trunk
Files:
8 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/gui/apps/launcher.tcl

    r6296 r6473  
    11#!/bin/sh
    2 # -*- mode: Tcl -*-
     2# -*- mode: tcl; indent-tabs-mode: nil -*-
    33# ----------------------------------------------------------------------
    44#  RAPPTURE LAUNCHER
     
    4444
    4545set rapptureInfo(cwd) [pwd]
     46set rapptureInfo(appName) ""
    4647
    4748if {[info exists env(TOOL_PARAMETERS)]} {
     
    8081                            lappend params(load) $value
    8182                            set params(opt) "-load"
     83                        }
     84                        "(simset)" - "" {
     85                            lappend params(simset) $value
     86                            set params(opt) "-simset"
    8287                        }
    8388                        "(execute)" {
     
    176181                }
    177182            }
     183            -simset {
     184                lappend alist $opt [lindex $argv 0]
     185                set argv [lrange $argv 1 end]
     186            }
     187            -appname {
     188                global rapptureInfo
     189                set rapptureInfo(appName) [lindex $argv 0]
     190                set argv [lrange $argv 1 end]
     191            }
    178192            default {
    179193                puts stderr "usage:"
     
    202216            set reqpkgs Tk
    203217        }
     218        -simset {
     219            # add tool parameters to the end of any files given on cmd line
     220            set loadlist [concat $loadlist $params(simset)]
     221            set alist [concat $alist -simset $loadlist]
     222
     223            package require RapptureGUI
     224            set guidir $RapptureGUI::library
     225            set mainscript [file join $guidir scripts main.tcl]
     226            set reqpkgs Tk
     227        }
    204228        -execute {
    205229            if {[llength $params(execute)] != 1} {
     
    260284# Note: We're sourcing the driver file "main.tcl" rather than exec-ing
    261285#       wish because we want to see stderr and stdout messages when they
    262 #       are written, rather than when the program completes.  It also
    263 #       eliminates one process waiting for the other to complete. If
    264 #       "exec" is needed, then the following could be replaced with
    265 #       blt::bgexec.  It doesn't try to redirect stderr into a file.
     286#       are written, rather than when the program completes.  It also
     287#       eliminates one process waiting for the other to complete. If
     288#       "exec" is needed, then the following could be replaced with
     289#       blt::bgexec.  It doesn't try to redirect stderr into a file.
    266290set argv $alist
    267291foreach name $reqpkgs {
  • trunk/gui/scripts/analyzer.tcl

    r6030 r6473  
    6060    public method reset {{when -eventually}}
    6161    public method load {xmlobj}
     62    public method reload { fileName }
    6263    public method clear {{xmlobj "all"}}
    6364    public method download {option args}
     65    public method buildMenu { w url appName }
    6466
    6567    protected method _plot {args}
     
    8385    protected method _adjust_level {win}
    8486
     87    private variable _done 0
     88    private variable _tree ""
     89    private variable _saved
     90    private variable _name ""
     91    private variable _revision 0;       # Tool revision
    8592    private variable _tool ""          ;# belongs to this tool
    8693    private variable _appName ""       ;# Name of application
     
    94101    private variable _plotlist ""      ;# items currently being plotted
    95102    private variable _lastPlot
    96     private common job                 ;# array var used for blt::bgexec jobs
    97103    private variable _uq_active 0      ;# a UQ variables has been used
    98104    private variable _wait_uq 0
     105    private common job                 ;# array var used for blt::bgexec jobs
     106
     107    private method BuildQuestionDialog { popup }
     108    private method BuildSimulationTable { w }
     109    private method Cancel {}
     110    private method CheckSimsetDetails {}
     111    private method CleanName { name }
     112    private method CreateSharedPath { path }
     113    private method EditSimset {}
     114    private method ExportFile { src msgFile }
     115    private method ExportURL { appName installdir }
     116    private method FindSimsetsForApp { appName }
     117    private method GetSimset { name }
     118    private method InstallSharedFile { installdir file }
     119    private method LoadSimulations { runfiles }
     120    private method Ok {}
     121    private method OverwriteSaveFile {}
     122    private method PostMenu { menu }
     123    private method ReadSimsetFile { fileName }
     124    private method Save {}
     125    private method SaveSimulations {}
     126    private method SelectSimsetForDeletion {}
     127    private method SelectSimsetForLoading {}
     128    private method SelectSimsetNameAndSave {}
     129    private method SelectSimsetToShare {}
     130    private method WriteSimsetFile { appName fileName share }
    99131}
    100132
     
    108140itcl::body Rappture::Analyzer::constructor {tool args} {
    109141    set _tool $tool
     142    set _tree [blt::tree create]
    110143
    111144    # use this to store all simulation results
     
    149182
    150183    # if there's a hub url, then add "About" and "Questions" links
    151     set _appName [$_tool xml get tool.id]
     184    global rapptureInfo
     185    if { $rapptureInfo(appName) == "" } {
     186        set _appName [$_tool xml get "tool.id"]
     187    } else {
     188        set _appName $rapptureInfo(appName)
     189    }
     190    set num [$_tool xml get "tool.version.application.revision"]
     191    if { $num == "" } {
     192        set num 0
     193    }
     194    set _revision $num
    152195    set url [Rappture::Tool::resources -huburl]
    153     if {"" != $url && "" != $_appName} {
    154         itk_component add hubcntls {
    155             frame $itk_component(simbg).hubcntls
    156         } {
    157             usual
    158             rename -background -simcontrolcolor simControlColor Color
    159         }
    160         pack $itk_component(hubcntls) -side right -padx 4
    161 
    162         itk_component add icon {
    163             label $itk_component(hubcntls).icon -image [Rappture::icon ask] \
    164                 -highlightthickness 0
    165         } {
    166             usual
    167             ignore -highlightthickness
    168             rename -background -simcontrolcolor simControlColor Color
    169         }
    170         pack $itk_component(icon) -side left
    171 
    172         itk_component add about {
    173             button $itk_component(hubcntls).about -text "About this tool" \
    174                 -command [list Rappture::filexfer::webpage \
    175                               "$url/tools/$_appName"]
    176         } {
    177             usual
    178             ignore -font
    179             rename -background -simcontrolcolor simControlColor Color
    180             rename -highlightbackground -simcontrolcolor simControlColor Color
    181         }
    182         pack $itk_component(about) -side top -anchor w
    183 
    184         itk_component add questions {
    185             button $itk_component(hubcntls).questions -text Questions? \
    186                 -command [list Rappture::filexfer::webpage \
    187                               "$url/resources/$_appName/questions"]
    188         } {
    189             usual
    190             ignore -font
    191             rename -background -simcontrolcolor simControlColor Color
    192             rename -highlightbackground -simcontrolcolor simControlColor Color
    193         }
    194         pack $itk_component(questions) -side top -anchor w
    195     }
    196 
     196
     197    itk_component add hubcntls {
     198        frame $itk_component(simbg).hubcntls
     199    } {
     200        usual
     201        rename -background -simcontrolcolor simControlColor Color
     202    }
     203    pack $itk_component(hubcntls) -side right -padx 4
     204   
     205    itk_component add help {
     206        menubutton $itk_component(hubcntls).help \
     207            -image [Rappture::icon hamburger_menu] \
     208            -highlightthickness 0 \
     209            -menu $itk_component(hubcntls).help.menu
     210    } {
     211        usual
     212        ignore -highlightthickness
     213        rename -background -simcontrolcolor simControlColor Color
     214    }
     215    pack $itk_component(help) -side left
     216    buildMenu $itk_component(help).menu $url $_appName
     217   
    197218    itk_component add simstatus {
    198219        text $itk_component(simbg).simstatus -borderwidth 0 \
     
    399420# ----------------------------------------------------------------------
    400421itcl::body Rappture::Analyzer::simulate {args} {
    401     #puts "simulate args='$args'"
    402422
    403423    set uq [$_tool get_uq -uq_type smolyak -uq_args 2]
     
    426446        # check to see if simulation is really needed
    427447        $_tool sync
    428         if {[$_resultset contains [$_tool xml object]]
    429               && ![string equal $_control "manual-resim"]} {
     448        if {[$_resultset contains [$_tool xml object]] &&
     449            ![string equal $_control "manual-resim"]} {
    430450            # not needed -- show results and return
    431451            $itk_component(notebook) current analyze
     
    518538    # check to see if simulation is really needed
    519539    $_tool sync
    520     if {![$_resultset contains [$_tool xml object]]
    521           || [string equal $_control "manual-resim"]} {
     540    if {![$_resultset contains [$_tool xml object]] ||
     541        [string equal $_control "manual-resim"]} {
    522542        # if control mode is "auto", then simulate right away
    523543        if {[string match auto* $_control]} {
     
    11101130    # Scan through and pick out any =RAPPTURE-PROGRESS=> messages first.
    11111131    #
    1112 
    1113     while {[regexp -indices \
    1114                {=RAPPTURE-PROGRESS=> *([-+]?[0-9]+) +([^\n]*)(\n|$)} $message \
    1115                 match percent mesg]} {
     1132    set pattern {=RAPPTURE-PROGRESS=> *([-+]?[0-9]+) +([^\n]*)(\n|$)}
     1133    while {[regexp -indices $pattern $message match percent mesg]} {
    11161134
    11171135        foreach {i0 i1} $percent break
     
    11551173    #
    11561174    $itk_component(runinfo) configure -state normal
    1157 
    1158     while {[regexp -indices \
    1159                {=RAPPTURE-([a-zA-Z]+)=>([^\n]*)(\n|$)} $message \
    1160                 match type mesg]} {
    1161 
     1175    set pattern {=RAPPTURE-([a-zA-Z]+)=>([^\n]*)(\n|$)}
     1176    while {[regexp -indices $pattern $message match type mesg]} {
    11621177        foreach {i0 i1} $match break
    11631178        set first [string range $message 0 [expr {$i0-1}]]
     
    12021217    set name [$itk_component(viewselector) value]
    12031218    if {[info exists _label2desc($name)] &&
    1204          [string length $_label2desc($name)] > 0} {
     1219        [string length $_label2desc($name)] > 0} {
    12051220        append tip "$_label2desc($name)\n\n"
    12061221    }
     
    14701485            continue
    14711486        }
    1472         if 0 {
    1473         # Recurse over all child nodes.
    1474         _trajToSequence $xmlobj $current
    1475         }
    14761487    }
    14771488}
     
    14981509    _fixNotebook
    14991510}
     1511
     1512itcl::body Rappture::Analyzer::PostMenu { m } {
     1513    if { [FindSimsetsForApp $_appName] == 0 } {
     1514        $m entryconfigure "Load Simulations" -state disable
     1515        $m entryconfigure "Share Simulations" -state disable
     1516        $m entryconfigure "Delete Simulations" -state disabled
     1517    } else {
     1518        $m entryconfigure "Load Simulations" -state normal
     1519        $m entryconfigure "Share Simulations" -state normal
     1520        $m entryconfigure "Delete Simulations" -state normal
     1521    }
     1522    if { [$_resultset size] == 0 } {
     1523        $m entryconfigure "Save Simulations" -state disabled
     1524    } else {
     1525        $m entryconfigure "Save Simulations" -state normal
     1526    }
     1527}
     1528       
     1529#
     1530# Build Help menu
     1531#
     1532itcl::body Rappture::Analyzer::buildMenu { m url appName } {
     1533    menu $m \
     1534        -tearoff 0 \
     1535        -postcommand [itcl::code $this PostMenu $m]
     1536    if { $appName == "" || $url == "" } {
     1537        set state disabled
     1538    } else {
     1539        set state normal
     1540    }
     1541    set webcmd Rappture::filexfer::webpage
     1542    set group "app-$_appName"
     1543    if { [info exists env(SESSION)] }  {
     1544        set referer "&referrer=$url/tools/$appName/session?sess=$env(SESSION)"
     1545    } else {
     1546        set referer ""
     1547    }
     1548    $m add command -label "About this tool" \
     1549        -command [list $webcmd "$url/tools/$appName"] \
     1550        -state $state
     1551    $m add command -label "Questions?" \
     1552        -command [list $webcmd "$url/resources/$_appName/questions"] \
     1553        -state $state
     1554    $m add command -label "Tickets" -state $state \
     1555        -command [list $webcmd "$url/feedback/report_problems?group=$group$referer"]
     1556    $m add command -label "Wish List" -state disabled
     1557    $m add separator
     1558    $m add command -label "Save Simulations" \
     1559        -command [itcl::code $this SelectSimsetNameAndSave] \
     1560        -state $state
     1561    $m add command -label "Load Simulations" \
     1562        -command [itcl::code $this SelectSimsetForLoading] \
     1563        -state $state
     1564    $m add command -label "Delete Simulations" \
     1565        -command [itcl::code $this SelectSimsetForDeletion] \
     1566        -state $state
     1567    $m add command -label "Share Simulations" \
     1568        -command [itcl::code $this SelectSimsetToShare] \
     1569        -state $state
     1570    return $m
     1571}
     1572
     1573itcl::body Rappture::Analyzer::BuildSimulationTable { top } {
     1574    # All weirdness is due to the Resultset object.
     1575
     1576    # First create nodes for each simulation.  The node label is the
     1577    # simulation number (such as #1, #2, etc). 
     1578    eval $_tree delete 0
     1579    set labels "selected simnum"
     1580    foreach {name index} [$_resultset diff values simnum] {
     1581        set node [$_tree insert 0 -label $name]
     1582        $_tree set $node "simnum" $name
     1583        set index2name($index) $name
     1584       
     1585    }
     1586    # Next fill in the xmlobj associated with each simulation.  We assume
     1587    # that the order is the the same as the simulation number.
     1588    set index 0
     1589    foreach {xmlobj dummy} [$_resultset diff values xmlobj]  {
     1590        set node [$_tree findchild 0 $index2name($index)]
     1591        set runfile [$xmlobj get output.filename]
     1592        set version [$xmlobj get output.version]
     1593        $_tree set $node \
     1594            "selected" 1 "xmlobj" $xmlobj "runfile" $runfile "version" $version
     1595        incr index
     1596    }
     1597    # Finally for each different input, for each simulation add the
     1598    # label and value for the specific input.
     1599    foreach name [$_resultset diff names] {
     1600        # Ignore non-input names.
     1601        if { $name == "xmlobj" || $name == "simnum" } {
     1602            continue
     1603        }
     1604        foreach node [$_tree children 0] {
     1605            set xmlobj [$_tree get $node xmlobj]
     1606            set label [$xmlobj get $name.about.label]
     1607            set value [$xmlobj get $name.current]
     1608            if { [$_tree exists $node $label] } {
     1609                puts stderr "This can't be: $label already exists in $node"
     1610            }
     1611            if { [lsearch $labels $label] < 0 } {
     1612                lappend labels $label
     1613            }
     1614            $_tree set $node $label $value
     1615        }
     1616    }
     1617    frame $top
     1618    set tv $top.tv
     1619    blt::treeview $top.tv -tree $_tree \
     1620        -height 1i \
     1621        -xscrollcommand [list $top.xs set] \
     1622        -yscrollcommand [list $top.ys set] \
     1623        -font "Arial 10"
     1624    scrollbar $top.xs -orient horizontal -command "$tv xview"
     1625    scrollbar $top.ys -orient vertical -command "$tv yview"
     1626       
     1627    eval $tv column insert end $labels
     1628    $tv style checkbox checkbox -showvalue no
     1629    $tv column configure simnum -title "Simulation"
     1630    $tv column configure selected -title "Save" -style checkbox \
     1631        -width .5i -edit yes
     1632    $tv column configure treeView -hide yes
     1633    blt::table $top \
     1634        0,0 $top.tv -fill both \
     1635        0,1 $top.ys -fill y \
     1636        1,0 $top.xs -fill x
     1637    blt::table configure $top r* c* -resize none
     1638    blt::table configure $top r0 c0 -resize both
     1639}
     1640
     1641itcl::body Rappture::Analyzer::CleanName { name } {
     1642    regsub -all {/} $name {_} name
     1643    return $name
     1644}
     1645
     1646#
     1647# Get the results and display them in a table.
     1648#
     1649# Use the table to omit specific results
     1650# Use file chooser to save to specific
     1651itcl::body Rappture::Analyzer::SelectSimsetNameAndSave {} {
     1652    if { [EditSimset] } {
     1653        set name [CleanName $_saved(Name)]
     1654        set saveDir [file join ~/data/saved $_appName $name]
     1655        set saveDir [file normalize $saveDir]
     1656        file mkdir $saveDir
     1657        set fileName [file join $saveDir $name.sav]
     1658        if { [file exists $fileName] } {
     1659            if { ![OverwriteSaveFile] } {
     1660                return
     1661            }
     1662        }
     1663        WriteSimsetFile $_appName $fileName 0
     1664    }
     1665}
     1666
     1667#
     1668# Get the results and display them in a table.
     1669#
     1670# Use the table to omit specific results
     1671# Use file chooser to save to specific
     1672itcl::body Rappture::Analyzer::SelectSimsetToShare {} {
     1673    if { [GetSimset "share"] } {
     1674        global env
     1675        set name [CleanName $_saved(Name)]
     1676        set shareDir [file join /data/tools/shared $env(USER) $_appName $name]
     1677        if { [catch {
     1678            CreateSharedPath $shareDir
     1679        } errs] != 0 } {
     1680            puts stderr errs=$errs
     1681            return
     1682        }
     1683        set fileName [file join $shareDir $name.sav]
     1684        if { [file exists $fileName] } {
     1685            if { ![OverwriteSaveFile] } {
     1686                return
     1687            }
     1688        }
     1689        WriteSimsetFile $_appName $fileName 1
     1690        ExportURL $_appName $shareDir
     1691    }
     1692}
     1693
     1694#
     1695# Checks editor widgets to see if the name and description have been
     1696# set.  If this is so it enables the save button in the editor.
     1697#
     1698itcl::body Rappture::Analyzer::CheckSimsetDetails {} {
     1699    set popup .savesimset
     1700    if { ![winfo exists $popup] } {
     1701        return
     1702    }
     1703    set inner [$popup component inner]
     1704    set name [string trim [$inner.name get]]
     1705    set desc [string trim [$inner.desc get 0.0 end]]
     1706    $inner.controls.save configure -state disabled
     1707    if { $name == "" || $desc == "" } {
     1708        return
     1709    }
     1710    $inner.controls.save configure -state normal
     1711}
     1712
     1713#
     1714# Gathers all the information from the simset editor into the _saved array
     1715#
     1716itcl::body Rappture::Analyzer::SaveSimulations {} {
     1717    set popup .savesimset
     1718    if { ![winfo exists $popup] } {
     1719        return
     1720    }
     1721    set inner [$popup component inner]
     1722    array unset _saved
     1723    set name [string trim [$inner.name get]]
     1724    set desc [string trim [$inner.desc get 0.0 end]]
     1725    set _saved(Application) $_appName
     1726    set _saved(Revision) $_revision
     1727    set _saved(Date) [clock format [clock seconds]]
     1728    set _saved(Name) $name
     1729    set _saved(Description) $desc
     1730    set files {}
     1731    foreach node [$_tree children root] {
     1732        set fileName [$_tree get $node runfile]
     1733        lappend files $fileName
     1734    }
     1735    set _saved(Files) $files
     1736    set _done 1
     1737}
     1738
     1739itcl::body Rappture::Analyzer::Cancel {} {
     1740    set _done 0
     1741}
     1742
     1743itcl::body Rappture::Analyzer::Ok {} {
     1744    set popup .selectsimset
     1745    if { ![winfo exists $popup] } {
     1746        return
     1747    }
     1748    set inner [$popup component inner]
     1749    set tv $inner.tv
     1750    if { ![$tv selection present] } {
     1751        return
     1752    }
     1753    set node [$tv curselection]
     1754    array unset _saved
     1755    array set _saved [$_tree get $node]
     1756    set saveDir [file dirname $_saved(FileName)]
     1757    set files {}
     1758    foreach file $_saved(Files) {
     1759        if { [file pathtype $file] == "relative" } {
     1760            set file [file join $saveDir $file]
     1761        }
     1762        lappend files $file
     1763    }
     1764    set _saved(Files) $files
     1765    set _done 1
     1766}
     1767
     1768#
     1769# WriteSimsetFile --
     1770#
     1771#       Write the .sav file and the runfiles in the
     1772#            ~/data/saved/$_appName/$name
     1773#       directory. 
     1774# The run files will be in the same directory as the .sav file. 
     1775# For example, if the simset file is /path/to/appName/myName/myName.sav,
     1776# the runfile directory is /path/to/appName/myName.
     1777#
     1778itcl::body Rappture::Analyzer::WriteSimsetFile { appName fileName share } {
     1779    set saveDir [file dirname $fileName]
     1780    if { [file exists $saveDir] } {
     1781        file delete -force $saveDir
     1782    }
     1783    if { [catch {
     1784        if { $share } {
     1785            CreateSharedPath $saveDir
     1786        } else {
     1787            file mkdir $saveDir
     1788        }
     1789        set f [open $fileName "w"]
     1790        puts $f [list "Name"        $_saved(Name)]
     1791        puts $f [list "Description" $_saved(Description)]
     1792        puts $f [list "Date"        $_saved(Date)]
     1793        global env
     1794        puts $f [list "Creator"     $env(USER)]
     1795        puts $f [list "Application" $_saved(Application)]
     1796        puts $f [list "Revision"    $_saved(Revision)]
     1797        set runfiles ""
     1798        foreach file $_saved(Files) {
     1799            set tail [file tail $file]
     1800            set dest [file join $saveDir $tail]
     1801            if { $share } {
     1802                InstallSharedFile $saveDir $file
     1803            } else {
     1804                file copy -force $file $dest
     1805            }
     1806            lappend runfiles $tail
     1807        }
     1808        puts $f [list "Files" $runfiles]
     1809        close $f
     1810        if { $share } {
     1811            file attributes $fileName -permissions o+r,g+rw
     1812        }
     1813    } errs] != 0 } {
     1814        global errorInfo
     1815        puts stderr "errs=$errs errorInfo=$errorInfo"
     1816    }
     1817}
     1818
     1819itcl::body Rappture::Analyzer::EditSimset {} {
     1820    set popup .savesimset
     1821    if { [winfo exists $popup] } {
     1822        destroy $popup
     1823    }
     1824    # Create a popup for the print dialog
     1825    Rappture::Balloon $popup -title "Save set of simulations..."
     1826    set inner [$popup component inner]
     1827
     1828    label $inner.name_l -text "Simulation Set Name"
     1829    entry $inner.name -background white
     1830    label $inner.desc_l -text "Simulation Set Description" -height 5
     1831    text $inner.desc  -background white
     1832    label $inner.select_l -text "Selected Simulations"
     1833    BuildSimulationTable $inner.select
     1834    frame $inner.controls
     1835    bind $inner.desc <KeyPress> [itcl::code $this CheckSimsetDetails]
     1836    bind $inner.name <KeyPress> [itcl::code $this CheckSimsetDetails]
     1837    bind $inner.desc <Motion> [itcl::code $this CheckSimsetDetails]
     1838    bind $inner.name <Motion> [itcl::code $this CheckSimsetDetails]
     1839    button $inner.controls.cancel -text "Cancel" \
     1840        -command [itcl::code $this Cancel]
     1841    button $inner.controls.save -text "Save" \
     1842        -command [itcl::code $this SaveSimulations]
     1843    blt::table $inner.controls \
     1844        0,0 $inner.controls.cancel \
     1845        0,1 $inner.controls.save
     1846   
     1847    blt::table $inner \
     1848        0,0 $inner.name_l -anchor ne \
     1849        0,1 $inner.name -fill x \
     1850        1,0 $inner.desc_l -anchor ne \
     1851        1,1 $inner.desc -fill both \
     1852        2,0 $inner.select_l -anchor ne \
     1853        2,1 $inner.select -fill both \
     1854        3,1 $inner.controls  -fill x
     1855    blt::table configure $inner r1 -height 1i
     1856    blt::table configure $inner r2 -pad 20
     1857    $popup configure \
     1858        -deactivatecommand [itcl::code $this Cancel]
     1859
     1860    set inner [$popup component inner]
     1861    if {[$_resultset size] > 1} {
     1862        blt::table $inner \
     1863            2,0 $inner.select_l -anchor ne \
     1864            2,1 $inner.select -fill both
     1865    } else {
     1866        blt::table forget $inner.select $inner.select_l
     1867    }
     1868    CheckSimsetDetails
     1869    update
     1870    # Activate the popup and call for the output.
     1871    $popup activate $itk_component(help) below
     1872    Cancel
     1873    tkwait variable [itcl::scope _done]
     1874    set doSave $_done
     1875    $popup deactivate
     1876    return $doSave
     1877}
     1878
     1879
     1880itcl::body Rappture::Analyzer::ReadSimsetFile { fileName } {
     1881    if { [catch {
     1882        set f [open $fileName "r"]
     1883        set contents [read $f]
     1884        close $f
     1885        array set _saved $contents
     1886    } errs] != 0 } {
     1887        return 0
     1888    }
     1889    if { ![info exists _saved(Files)] } {
     1890        return 0
     1891    }
     1892    set saveDir [file dirname $fileName]
     1893    foreach file $_saved(Files) {
     1894        if { [file pathtype $file] == "relative" } {
     1895            set file [file join $saveDir $file]
     1896        }
     1897        if { ![file readable $file] } {
     1898            puts stderr "runfile $file isn't readable"
     1899            return 0
     1900        }
     1901    }
     1902    set _saved(FileName) $fileName
     1903    return 1
     1904}
     1905
     1906#
     1907# Run files can be either explicitly named with their absolute or
     1908# relative path.  If the path is relative it is assumed the parent
     1909# directory is relative to the simset file.
     1910#
     1911#   /my/path/to/myfile.sav
     1912#   /my/path/to/myfile/runfiles...
     1913#
     1914itcl::body Rappture::Analyzer::LoadSimulations { files } {
     1915    set loadobjs {}
     1916    set saveDir [file dirname $_saved(FileName)]
     1917    foreach runfile $files {
     1918        if { [file pathtype $runfile] == "relative" } {
     1919            set runfile [file join $saveDir $runfile]
     1920        }
     1921        if { ![file exists $runfile] } {
     1922            puts stderr "can't find run: \"$runfile\""
     1923            continue
     1924        }
     1925        set status [catch {Rappture::library $runfile} result]
     1926        lappend loadobjs $result
     1927    }
     1928    clear
     1929    foreach runobj $loadobjs {
     1930        load $runobj
     1931    }
     1932    configure -notebookpage analyze
     1933    global win
     1934    $win.pager configure -nosim 1
     1935    $win.pager current analyzer
     1936    $win.pager configure -nosim 0
     1937}
     1938
     1939#
     1940# Delete selected simulation set.
     1941#
     1942itcl::body Rappture::Analyzer::SelectSimsetForDeletion {} {
     1943    if { [FindSimsetsForApp $_appName] == 0 } {
     1944        return
     1945    }
     1946    if { [GetSimset "delete"] } {
     1947        set saveDir [file dirname $_saved(FileName)]
     1948        file delete -force $saveDir
     1949    }
     1950}
     1951
     1952#
     1953# Find all .sav files for an application and load them into the tree.
     1954#
     1955itcl::body Rappture::Analyzer::FindSimsetsForApp { appName } {
     1956    if { $appName == "" } {
     1957        puts stderr "No application name found"
     1958        return 0
     1959    }
     1960    $_tree delete 0
     1961    foreach fileName [glob -nocomplain ~/data/saved/$appName/*/*.sav] {
     1962        if { [ReadSimsetFile $fileName] } {
     1963            if { $_revision > 0 && [info exists _saved(Revision)] &&
     1964                 $_revision != $_saved(Revision) } {
     1965                continue;               # Revision doesn't match.
     1966            }
     1967            $_tree insert 0 -label $_saved(Name) -data [array get _saved]
     1968        }
     1969    }
     1970    return [$_tree degree 0]
     1971}
     1972
     1973#
     1974# Create dialog to select a simset from the currently available simsets.
     1975#
     1976itcl::body Rappture::Analyzer::GetSimset { name } {
     1977    set popup .selectsimset
     1978    if { [winfo exists $popup] } {
     1979        destroy $popup
     1980    }
     1981    # Create a popup for the print dialog
     1982    set title [string tolower $name]
     1983    Rappture::Balloon $popup -title "Select set of simulations to $title..."
     1984    set inner [$popup component inner]
     1985   
     1986    set tv $inner.tv
     1987    blt::treeview $inner.tv -tree $_tree \
     1988        -height 1i \
     1989        -width 5i \
     1990        -xscrollcommand [list $inner.xs set] \
     1991        -yscrollcommand [list $inner.ys set] \
     1992        -font "Arial 10"
     1993    scrollbar $inner.xs -orient horizontal -command "$tv xview"
     1994    scrollbar $inner.ys -orient vertical -command "$tv yview"
     1995    frame $inner.controls
     1996   
     1997    $tv column insert end "Name" "Description" "Date" "Creator"
     1998    $tv column configure treeView -hide yes
     1999    blt::table $inner \
     2000        0,0 $inner.tv -fill both \
     2001        0,1 $inner.ys -fill y \
     2002        1,0 $inner.xs -fill x
     2003    blt::table configure $inner r* c* -resize none
     2004    blt::table configure $inner r0 c0 -resize both
     2005
     2006    button $inner.controls.cancel -text "Cancel" \
     2007        -command [itcl::code $this Cancel]
     2008    set title [string totitle $name]
     2009    button $inner.controls.save -text $title \
     2010        -command [itcl::code $this Ok]
     2011    blt::table $inner.controls \
     2012        0,0 $inner.controls.cancel \
     2013        0,1 $inner.controls.save
     2014   
     2015    blt::table $inner \
     2016        0,0 $inner.tv -fill both \
     2017        0,1 $inner.ys -fill y \
     2018        1,0 $inner.xs -fill x \
     2019        3,0 $inner.controls  -fill x -cspan 2
     2020    blt::table configure $inner r* c* -resize none
     2021    blt::table configure $inner r0 c0 -resize both
     2022    blt::table configure $inner r2 -height 0.1i
     2023   
     2024    $popup configure \
     2025        -deactivatecommand [itcl::code $this Cancel]
     2026
     2027    $tv selection set [$_tree firstchild 0]
     2028    set inner [$popup component inner]
     2029    update
     2030    # Activate the popup and call for the output.
     2031    $popup activate $itk_component(help) below
     2032    Cancel
     2033    tkwait variable [itcl::scope _done]
     2034    set result $_done
     2035    $popup deactivate
     2036    return $result
     2037}
     2038
     2039itcl::body Rappture::Analyzer::SelectSimsetForLoading {} {
     2040    if { [FindSimsetsForApp $_appName] == 0 } {
     2041        return
     2042    }
     2043    if { [GetSimset "load"] } {
     2044        LoadSimulations $_saved(Files)
     2045    }
     2046}
     2047
     2048itcl::body Rappture::Analyzer::reload { fileName } {
     2049    if { [catch {
     2050        ReadSimsetFile $fileName
     2051        if { [llength $_saved(Files)] > 0 } {
     2052            #StartSplashScreen
     2053            LoadSimulations $_saved(Files)
     2054            #HideSplashScreen
     2055        }
     2056    } errs] != 0 } {
     2057        puts stderr "can't load \"$fileName\": errs=$errs"
     2058    }
     2059}
     2060
     2061itcl::body Rappture::Analyzer::BuildQuestionDialog { popup } {
     2062    toplevel $popup -background grey92 -bd 2 -relief raised
     2063    wm withdraw $popup
     2064    wm overrideredirect $popup true
     2065    set inner $popup
     2066    # Create the print dialog widget and add it to the the balloon popup.
     2067    label $popup.title -text " " \
     2068        -padx 10 -pady 10 \
     2069        -background grey92
     2070    button $popup.yes -text "Yes" \
     2071        -highlightthickness 0 \
     2072        -bd 2  \
     2073        -command [itcl::code $this Save]
     2074    button $popup.no -text "No" \
     2075        -highlightthickness 0 \
     2076        -bd 2 \
     2077        -command [itcl::code $this Cancel]
     2078    blt::table $popup \
     2079        0,0 $popup.title -cspan 2  -fill x -pady 4 \
     2080        1,0 $popup.yes -width { 0 1i .6i } -pady 4 \
     2081        1,1 $popup.no -width { 0 1i .6i }  -pady 4
     2082    blt::table configure $popup  r2 -height 0.125i
     2083}
     2084
     2085itcl::body Rappture::Analyzer::Save {} {
     2086    set _done 1
     2087}
     2088
     2089itcl::body Rappture::Analyzer::OverwriteSaveFile {} {
     2090    set popup .question
     2091    if { ![winfo exists $popup] } {
     2092        BuildQuestionDialog $popup
     2093    }
     2094    set text "Simulation set \"$_saved(Name)\" already exists. Overwrite?"
     2095    $popup.title configure -text $text
     2096    $popup.yes configure -text "Save"
     2097    $popup.no configure -text "Cancel"
     2098    set main [winfo toplevel $itk_component(help)]
     2099    set pw [winfo reqwidth $popup]
     2100    set ph [winfo reqheight $popup]
     2101    set sw [winfo reqwidth $main]
     2102    set sh [winfo reqheight $main]
     2103    set rootx [winfo rootx $main]
     2104    set rooty [winfo rooty $main]
     2105    set x [expr $rootx + (($sw - $pw) / 2)]
     2106    set y [expr $rooty + (($sh - $ph) / 2)]
     2107    wm geometry $popup +$x+$y
     2108    wm deiconify $popup
     2109    update
     2110    grab $popup
     2111    # Activate the popup and call for the output.
     2112    Cancel
     2113    tkwait variable [itcl::scope _done]
     2114    set doSave $_done
     2115    grab release $popup
     2116    destroy $popup
     2117    return $doSave
     2118}
     2119
     2120itcl::body Rappture::Analyzer::InstallSharedFile { installdir file } {
     2121    set dst [file join $installdir [file tail $file]]
     2122    file copy -force $file $dst
     2123    file attributes $dst -permissions g+rw,o+r
     2124    return $dst
     2125}
     2126
     2127itcl::body Rappture::Analyzer::CreateSharedPath { path } {
     2128    set dir ""
     2129    foreach file [file split $path] {
     2130        set dir [file join $dir $file]
     2131        if { [file exists $dir] } {
     2132            if { ![file isdirectory $dir] } {
     2133                error "error in path \"$path\": \"$dir\" is not a directory."
     2134            }
     2135        } else {
     2136            file mkdir $dir
     2137            file attributes $dir -permissions g+rwx,o+rx
     2138        }
     2139    }
     2140}
     2141
     2142itcl::body Rappture::Analyzer::ExportURL { appName installdir } {
     2143    set fileName [file join $installdir url.txt]
     2144    set name [file tail $installdir]
     2145    set f [open $fileName "w"]
     2146    puts $f "https://nanohub.org/tools/$appName/invoke?params=file(simset):$installdir/$name.sav\n"
     2147    close $f
     2148    file attributes $fileName -permissions g+rw,o+r
     2149    if { [file exists /usr/bin/exportfile] } {
     2150        set msgFile [file join $::Rappture::installdir export.html]
     2151        ExportFile $fileName $msgFile
     2152    }
     2153}
     2154
     2155itcl::body Rappture::Analyzer::ExportFile { src msgFile } {
     2156    set dstdir ~/.filexfer/exportfile
     2157    file mkdir $dstdir
     2158    set dst [file join $dstdir [file tail $src]]
     2159    if { [file exists $dst] } {
     2160        puts stderr "could be a problem: already waiting to export $dst"
     2161    }
     2162    file copy -force $src $dst
     2163    # Now export the file.
     2164    if { [catch {
     2165        global exportFileVar
     2166        set exportFileVar 0;            # Kill any previous export.
     2167        set mesg "Select file to export to desktop/laptop"
     2168        update
     2169        set cmd "/usr/bin/exportfile --delete $dst --timeout 3 "
     2170        if { [file exists $msgFile] } {
     2171            append cmd "--message $msgFile"
     2172        }
     2173        blt::bgexec exportFileVar /bin/sh -c $cmd &
     2174    } errs]  != 0 } {
     2175        global errorInfo
     2176        puts stderr "err: $errorInfo"
     2177        file delete -force $dst
     2178    }
     2179}
     2180
  • trunk/gui/scripts/loader.tcl

    r5659 r6473  
    3838
    3939    private method SetDefaultValue { value }
     40    private method EventuallySetDefaultValue { value }
    4041
    4142    private variable _owner ""    ;# thing managing this control
     
    246247    set str [string trim [$_owner xml get $path.default]]
    247248    if { $str != "" } {
    248         bind $itk_component(hull) <Map> [itcl::code $this SetDefaultValue $str]
     249        EventuallySetDefaultValue $str
    249250    }
    250251}
     
    285286        $itk_component(combo) value $newval
    286287        return $newval
    287 
    288288    } elseif {[llength $args] != 0} {
    289289        error "wrong # args: should be \"value ?-check? ?newval?\""
     
    323323}
    324324
    325 #
    326 # SetDefaultValue --
    327 #
    328 #       Sets the designated default value for the loader.  This must be done
    329 #       after the entire application is assembled, otherwise the default
    330 #       values set up by the loader will be overwritten by the various widgets
    331 #       themselves when they try to set their default values.
    332 #
    333 #       This is called from a  <Map> event to the loader (combobox).  This
    334 #       will get trigger the first time the loader is displayed.  The binding
    335 #       is then removed.
    336 #
    337325itcl::body Rappture::Loader::SetDefaultValue { value } {
    338     after idle [itcl::code $this value $value]
    339     # We're done. Remove the binding.
    340     bind $itk_component(hull) <Map> {}
     326    $itk_component(combo) value $value
     327    _newValue
     328}
     329#
     330#
     331# EventuallySetDefaultValue --
     332#
     333#   Sets the designated default value for the loader.  This must be done
     334#   after the entire application is assembled, otherwise the default values
     335#   set up by the loader will be overwritten by the various widgets
     336#   themselves when they try to set their default values.
     337#
     338itcl::body Rappture::Loader::EventuallySetDefaultValue { value } {
     339    after 100 [itcl::code $this SetDefaultValue $value]
    341340}
    342341
  • trunk/gui/scripts/main.tcl

    r6022 r6473  
    9494    list  -load ""
    9595    value -input ""
     96    value -simset ""
    9697    value -nosim 0
    9798}
     
    260261
    261262#
    262 # Add a place for about/questions in the breadcrumbs area of this pager.
     263# Add a place for menu button in the breadcrumbs area of this pager.
    263264#
    264265set app [string trim [$tool xml get tool.id]]
    265266set url [Rappture::Tool::resources -huburl]
    266 if {"" != $url && "" != $app} {
    267     set f [$win.pager component breadcrumbarea]
    268     frame $f.hubcntls
    269     pack $f.hubcntls -side right -padx 4
    270     label $f.hubcntls.icon -image [Rappture::icon ask] -highlightthickness 0
    271     pack $f.hubcntls.icon -side left
    272     button $f.hubcntls.about -text "About this tool" -command "
    273         [list Rappture::filexfer::webpage $url/tools/$app]
    274         Rappture::Logger::log help about
    275     "
    276     pack $f.hubcntls.about -side top -anchor w
    277     button $f.hubcntls.questions -text Questions? -command "
    278         [list Rappture::filexfer::webpage $url/resources/$app/questions]
    279         Rappture::Logger::log help questions
    280     "
    281     pack $f.hubcntls.questions -side top -anchor w
    282 }
     267set crumbs [$win.pager component breadcrumbarea]
     268frame $crumbs.hubcntls
     269pack $crumbs.hubcntls -side right -padx 4
     270
     271set m $crumbs.hubcntls.help.menu
     272menubutton $crumbs.hubcntls.help \
     273    -image [Rappture::icon hamburger_menu] \
     274    -highlightthickness 0 \
     275    -menu $m
     276pack $crumbs.hubcntls.help -side top -anchor w
    283277
    284278#
     
    339333
    340334$tool notify add analyzer * [list $f.analyze reset]
     335$f.analyze buildMenu $m $url $app
     336
    341337
    342338# ----------------------------------------------------------------------
     
    386382
    387383# load previous xml runfiles
     384update
    388385if {[llength $params(-load)] > 0} {
    389386    foreach runobj $loadobjs {
     
    398395    }
    399396    $f.analyze configure -notebookpage analyze
     397    $win.pager configure -nosim 1
    400398    $win.pager current analyzer
     399    $win.pager configure -nosim 0
     400} elseif {$params(-simset) ne ""} {
     401    $f.analyze reload $params(-simset)
    401402} elseif {$params(-input) ne ""} {
    402403    $tool load $inputobj
     
    407408
    408409foreach path [array names ::Rappture::parameters] {
     410    if { $path == "simset" } {
     411        continue;                       # Don't consider file(simset)
     412    }
    409413    set fname $::Rappture::parameters($path)
    410     if {[catch {
    411           set fid [open $fname r]
    412           set info [read $fid]
    413           close $fid}] == 0} {
    414 
    415         set w [$tool widgetfor $path]
    416         if {$w ne ""} {
    417             if {[catch {$w value [string trim $info]} result]} {
    418                 puts stderr "WARNING: bad tool parameter value for \"$path\""
    419                 puts stderr "  $result"
    420             }
    421         } else {
    422             puts stderr "WARNING: can't find control for tool parameter: $path"
    423         }
     414    if { ![file exists $fname] } {
     415        puts stderr "WARNING: tool parameters file \"$fname\" doesn't exist"
     416        continue;                       # Can't find parameters file
     417    }       
     418    if { ![file readable $fname] } {
     419        puts stderr "WARNING: can't read tool parameters file \"$fname\""
     420        continue;                       # Can't read parameters file
     421    }
     422    set f [open $fname r]
     423    set contents [read $f]
     424    close $f
     425   
     426    set w [$tool widgetfor $path]
     427    if { $w == "" } {
     428        puts stderr "WARNING: can't find control for tool parameter: $path"
     429        continue;                       # Can't find rappture control for path
     430    }
     431    if {[catch {$w value [string trim $info]} result]} {
     432        puts stderr "WARNING: bad tool parameter value for \"$path\""
     433        puts stderr "  $result"
    424434    }
    425435}
  • trunk/gui/scripts/pager.tcl

    r5659 r6473  
    6767    private variable _page2info      ;# maps page name => -frame,-title,-command
    6868    private variable _current ""     ;# page currently shown
     69    public variable nosim 0
    6970}
    7071
     
    374375            # invoke it now.
    375376            #
    376             if {"" != $_current
    377                   && [string length $_page2info($_current-command)] > 0} {
     377            if {"" != $_current && !$nosim &&
     378                  [string length $_page2info($_current-command)] > 0} {
    378379                uplevel #0 $_page2info($_current-command)
    379380            }
  • trunk/lang/tcl/scripts/task.tcl

    r6296 r6473  
    1919
    2020itcl::class Rappture::Task {
     21    private method CheckForCachedRunFile { driverFile }
     22    private method CollectUQResults {}
     23    private method ExecuteSimulationCommand { cmd }
     24    private method GetCommand {}
     25    private method GetDriverFile {}
     26    private method GetSignal { signal }
     27    private method GetSimulationCommand { driverFile }
     28    private method GetUQErrors {}
     29    private method GetUQSimulationCommand { driverFile }
     30    private method GetUQTemplateFile {}
     31    private method IsCacheable {}
     32    private method LogCachedSimulationUsage {}
     33    private method LogSimulationUsage {}
     34    private method LogSubmittedSimulationUsage {}
     35    private method SetCpuResourceLimit {}
     36
    2137    public variable logger ""
    2238    public variable jobstats Rappture::Task::MiddlewareTime
     
    3551    public method save {xmlobj {name ""}}
    3652
    37     protected method _mkdir {dir}
    38     protected method _output {data}
    39     protected method _log {args}
    40     protected method _build_submit_cmd {cmd tfile params_file}
    41     protected method _get_params {varlist uq_type uq_args}
    42     private method GetSignal { signal }
     53    protected method OnOutput {data}
     54    protected method Log {args}
     55    protected method BuildSubmitCommand {cmd tfile params_file}
     56    protected method GetParamsForUQ {}
    4357
    4458    private variable _xmlobj ""      ;# XML object with inputs/outputs
    4559    private variable _origxml ""     ;# copy of original XML (for reset)
    46     private variable _lastrun ""     ;# name of last run file
    4760    private variable _installdir ""  ;# installation directory for this tool
    4861    private variable _outputcb ""    ;# callback for tool output
    49     private common job               ;# array var used for blt::bgexec jobs
    5062    private common jobnum 0          ;# counter for unique job number
    51 
     63    private variable _uq
     64   
     65    private variable _job
     66   
    5267    # get global resources for this tool session
    5368    public proc resources {{option ""}}
    5469
    5570    public common _resources
    56     public proc setAppName {name}   { set _resources(-appname) $name }
    57     public proc setHubName {name}   { set _resources(-hubname) $name }
    58     public proc setHubURL {name}    { set _resources(-huburl) $name }
    59     public proc setSession {name}   { set _resources(-session) $name }
    60     public proc setJobPrt {name}    { set _resources(-jobprotocol) $name }
    61     public proc setResultDir {name} { set _resources(-resultdir) $name }
     71    public proc setAppName {name}    { set _resources(-appname) $name }
     72    public proc setHubName {name}    { set _resources(-hubname) $name }
     73    public proc setHubURL {name}     { set _resources(-huburl) $name }
     74    public proc setSession {name}    { set _resources(-session) $name }
     75    public proc setJobPrt {name}     { set _resources(-jobprotocol) $name }
     76    public proc setResultDir {name}  { set _resources(-resultdir) $name }
     77    public proc setCacheHosts {name} { set _resources(-cachehosts) $name }
    6278
    6379    # default method for -jobstats control
     
    7490        session_token     Rappture::Task::setSession \
    7591        job_protocol      Rappture::Task::setJobPrt \
    76         results_directory Rappture::Task::setResultDir
     92        results_directory Rappture::Task::setResultDir \
     93        cache_hosts       Rappture::Task::setCacheHosts
    7794}
    7895
     
    94111    }
    95112    set _installdir $installdir
     113    package require http
     114    package require tls
     115    http::register https 443 [list ::tls::socket -tls1 1]
    96116
    97117    eval configure $args
     
    137157    foreach {path val} $args {
    138158        if {$path == "-uq_type"} {
    139             set uq_type $val
     159            set _uq(type) $val
    140160        } elseif {$path == "-uq_args"} {
    141             set uq_args $val
     161            set _uq(args) $val
    142162        }
    143163    }
    144164    #set varlist [$_xmlobj uq_get_vars]
    145165    foreach {varlist num} [$_xmlobj uq_get_vars] break
    146     return [Rappture::UQ ::#auto $varlist $num $uq_type $uq_args]
     166    return [Rappture::UQ ::#auto $varlist $num $_uq(type) $_uq(args)]
    147167}
    148168
     
    173193
    174194    #
    175     # Make sure that we save the proper application name.
    176     # Actually, the best place to get this information is
    177     # straight from the "installtool" script, but just in
    178     # case we have an older tool, we should insert the
     195    # Make sure that we save the proper application name.  Actually, the
     196    # best place to get this information is straight from the "installtool"
     197    # script, but just in case we have an older tool, we should insert the
    179198    # tool name from the resources config file.
    180199    #
    181     if {[info exists _resources(-appname)]
    182           && $_resources(-appname) ne ""
    183           && [$_xmlobj get tool.name] eq ""} {
     200    if {[info exists _resources(-appname)] && $_resources(-appname) ne "" &&
     201        [$_xmlobj get tool.name] eq ""} {
    184202        $_xmlobj put tool.name $_resources(-appname)
    185203    }
     
    187205    # if there are any args, use them to override parameters
    188206    set _outputcb ""
    189     set uq_type ""
     207    set _uq(type) ""
    190208    foreach {path val} $args {
    191209        if {$path == "-output"} {
    192210            set _outputcb $val
    193211        } elseif {$path == "-uq_type"} {
    194             set uq_type $val
     212            set _uq(type) $val
    195213        } elseif {$path == "-uq_args"} {
    196             set uq_args $val
     214            set _uq(args) $val
    197215        } else {
    198216            $_xmlobj put $path.current $val
     
    200218    }
    201219
    202     foreach item {control output error} { set job($item) "" }
    203 
     220    # Initialize job array variables
     221    array set _job {
     222        control  ""
     223        exitcode 0
     224        mesg     ""
     225        runfile  ""
     226        stderr   ""
     227        stdout   ""
     228        success  0
     229        xmlobj   ""
     230    }
     231
     232    SetCpuResourceLimit
     233    set driverFile [GetDriverFile]
     234    set cached 0
     235    if { [IsCacheable] } {
     236puts stderr "Cache checking: [time {
     237        set cached [CheckForCachedRunFile $driverFile]
     238     } ]"
     239puts stderr "checking cache=$cached"
     240    }
     241    if { !$cached } {
     242        if { $_uq(type) != "" } {
     243            set _uq(tfile) [GetUQTemplateFile]
     244        }
     245        if { $_uq(type) == "" } {
     246            set cmd [GetSimulationCommand $driverFile]
     247        } else {
     248            set cmd [GetUQSimulationCommand $driverFile]
     249        }
     250        if { $cmd == "" } {
     251            puts stderr "cmd is empty"
     252            append mesg "There is no command specified by\n\n"
     253            append mesg "    <command>\n"
     254            append mesg "    </command>\n\n"
     255            append mesg "in the tool.xml file."
     256            return [list 1 $mesg]
     257        }
     258        Rappture::rusage mark
     259        if { ![ExecuteSimulationCommand $cmd] } {
     260            return [list 1 $_job(mesg)]
     261        }
     262        if { [resources -jobprotocol] == "submit" } {
     263            LogSubmittedSimulationUsage
     264        } else {
     265            LogSimulationUsage
     266        }
     267    } else {
     268        LogCachedSimulationUsage
     269    }
     270    if { $_job(success) } {
     271        file delete -force -- $driverFile
     272        Log run finished
     273        return [list 0 $_job(xmlobj)]
     274    } else {
     275        # See if the job was aborted.
     276        if {[regexp {^KILLED} $_job(control)]} {
     277            Log run aborted
     278            return [list 1 "ABORT"]
     279        }
     280        Log run failed [list 0 $_job(mesg)]
     281        return [list 1 $_job(mesg)]
     282    }
     283}
     284
     285# ----------------------------------------------------------------------
     286#  Turn the command string from tool.xml into the proper syntax to use
     287#  with a submit parameter sweep with a temlate file.  Proper quoting
     288# of the template file is necessary to prevent submit from being too smart
     289# and converting it to a full pathname.
     290# ----------------------------------------------------------------------
     291itcl::body Rappture::Task::BuildSubmitCommand {cmd tfile params_file} {
     292    set quote_next 0
     293    set newcmd "submit --progress submit --runName=puq -l -i @:$tfile -d $params_file"
     294    set cmds [split $cmd " "]
     295    for {set i 0} {$i < [llength $cmds]} {incr i} {
     296        set arg [lindex $cmds $i]
     297        if {$quote_next == 1} {
     298            set nc [string range $arg 0 0]
     299            if {$nc != "\""} {
     300                set arg "\"\\\"$arg\\\"\""
     301            }
     302        }
     303        if {$arg == "--eval"} {
     304            set quote_next 1
     305        } else {
     306            set quote_next 0
     307        }
     308        if {$arg == "@driver"} {
     309            set arg "\"\\\"$tfile\\\"\""
     310        }
     311        append newcmd " " $arg
     312    }
     313    regsub -all @driver $newcmd $tfile newcmd
     314    return $newcmd
     315}
     316
     317# ----------------------------------------------------------------------
     318# USAGE: abort
     319#
     320# Clients use this during a "run" to abort the current job.
     321# Kills the job and forces the "run" method to return.
     322# ----------------------------------------------------------------------
     323itcl::body Rappture::Task::abort {} {
     324    Log run abort
     325    set _job(control) "abort"
     326}
     327
     328# ----------------------------------------------------------------------
     329# USAGE: reset
     330#
     331# Resets all input values to their defaults.  Sometimes used just
     332# before a run to reset to a clean state.
     333# ----------------------------------------------------------------------
     334itcl::body Rappture::Task::reset {} {
     335    $_xmlobj copy "" from $_origxml ""
     336    foreach path [Rappture::entities -as path $_xmlobj input] {
     337        if {[$_xmlobj element -as type $path.default] ne ""} {
     338            set defval [$_xmlobj get $path.default]
     339            $_xmlobj put $path.current $defval
     340        }
     341    }
     342}
     343
     344# ----------------------------------------------------------------------
     345# USAGE: xml <subcommand> ?<arg> <arg> ...?
     346# USAGE: xml object
     347#
     348# Used by clients to manipulate the underlying XML data for this
     349# tool.  The <subcommand> can be any operation supported by a
     350# Rappture::library object.  Clients can also request the XML object
     351# directly by using the "object" subcommand.
     352# ----------------------------------------------------------------------
     353itcl::body Rappture::Task::xml {args} {
     354    if {"object" == $args} {
     355        return $_xmlobj
     356    }
     357    return [eval $_xmlobj $args]
     358}
     359
     360# ----------------------------------------------------------------------
     361# USAGE: save <xmlobj> ?<filename>?
     362#
     363# Used by clients to save the contents of an <xmlobj> representing
     364# a run out to the given file.  If <filename> is not specified, then
     365# it uses the -resultsdir and other settings to do what Rappture
     366# would normally do with the output.
     367# ----------------------------------------------------------------------
     368itcl::body Rappture::Task::save {xmlobj {filename ""}} {
     369    if {$filename eq ""} {
     370
     371        # If there's a results_directory defined in the resources file,
     372        # then move the run.xml file there for storage.
     373
     374        set rdir ""
     375        if {$resultdir eq "@default"} {
     376            if {[info exists _resources(-resultdir)]} {
     377                set rdir $_resources(-resultdir)
     378            } else {
     379                global rapptureInfo
     380                set rdir $rapptureInfo(cwd)
     381            }
     382        } elseif {$resultdir ne ""} {
     383            set rdir $resultdir
     384        }
     385
     386        # use the runfile name generated by the last run
     387        if {$_job(runfile) ne ""} {
     388            set filename [file join $rdir $_job(runfile)]
     389        } else {
     390            set filename [file join $rdir run.xml]
     391        }
     392    }
     393
     394    # add any last-minute metadata
     395    $xmlobj put output.time [clock format [clock seconds]]
     396
     397    $xmlobj put tool.version.rappture.version $::Rappture::version
     398    $xmlobj put tool.version.rappture.revision $::Rappture::build
     399    $xmlobj put output.filename $filename
     400    $xmlobj put output.version $Rappture::version
     401   
     402    if {[info exists ::tcl_platform(user)]} {
     403        $xmlobj put output.user $::tcl_platform(user)
     404    }
     405
     406    # save the output
     407    set rdir [file dirname $filename]
     408    file mkdir $rdir
     409
     410    set fid [open $filename w]
     411    puts $fid "<?xml version=\"1.0\"?>"
     412    puts $fid [$xmlobj xml]
     413    close $fid
     414
     415    Log output saved in $filename
     416}
     417
     418# ----------------------------------------------------------------------
     419# USAGE: OnOutput <data>
     420#
     421# Used internally to send each bit of output <data> coming from the
     422# tool onto the caller, so the user can see progress.
     423# ----------------------------------------------------------------------
     424itcl::body Rappture::Task::OnOutput {data} {
     425    if {[string length $_outputcb] > 0} {
     426        uplevel #0 $_outputcb [list $data]
     427    }
     428}
     429
     430# ----------------------------------------------------------------------
     431# USAGE: Log <cmd> <arg> <arg> ...
     432#
     433# Used internally to log interesting events during the run.  If the
     434# -logger option is set (to Rappture::Logger::log, or something like
     435# that), then the arguments to this method are passed along to the
     436# logger and written out to a log file.  Logging is off by default,
     437# so this method does nothing unless -logger is set.
     438# ----------------------------------------------------------------------
     439itcl::body Rappture::Task::Log {args} {
     440    if {[string length $logger] > 0} {
     441        uplevel #0 $logger [list $args]
     442    }
     443}
     444
     445# ----------------------------------------------------------------------
     446# USAGE: MiddlewareTime <key> <value> ...
     447#
     448# Used as the default method for reporting job status information.
     449# Implements the old HUBzero method of reporting job status info to
     450# stderr, which can then be picked up by the tool session container.
     451# Most tools use the "submit" command, which talks directly to a
     452# database to log job information, so this isn't really needed.  But
     453# it doesn't hurt to have this and it can be useful in some cases.
     454# ----------------------------------------------------------------------
     455itcl::body Rappture::Task::MiddlewareTime {args} {
     456    set line "MiddlewareTime:"
     457    foreach {key val} $args {
     458        append line " $key=$val"
     459    }
     460    puts stderr $line
     461}
     462
     463itcl::body Rappture::Task::IsCacheable {} {
     464    if { ![info exists _resources(-cachehosts)] ||
     465         $_resources(-cachehosts) == "" } {
     466puts stderr cachehosts=[info exists _resources(-cachehosts)]
     467        return 0
     468    }
     469if 0 {
     470    set state [$_xmlobj get "tool.cache"]
     471puts stderr "cache tag is \"$state\""
     472    if { $state == "" } {
     473        return 0
     474    }
     475} else {
     476    set state 1
     477}
     478    if { ![string is boolean $state] } {
     479        return 0
     480    }
     481    return $state
     482}
     483
     484#
     485# Send the list of parameters to a python program so it can call PUQ
     486# and get a CSV file containing the parameter values to use for the runs.
     487itcl::body Rappture::Task::GetParamsForUQ {} {
     488    set pid [pid]
     489    # puts "puq.sh get_params $pid $_uq(varlist) $_uq(type) $_uq(args)"
     490    if {[catch {
     491        exec puq.sh get_params $pid $_uq(varlist) $_uq(type) $_uq(args)
     492    } errs] != 0 } {
     493        error "get_params.py failed: $errs\n[GetUQErrors]"
     494    }
     495    return "params${pid}.csv"
     496}
     497
     498itcl::body Rappture::Task::SetCpuResourceLimit {} {
    204499    # Set limits for cpu time
    205500    set limit [$_xmlobj get tool.limits.cputime]
     
    216511    }
    217512    Rappture::rlimit set cputime $limit
    218 
    219     # write out the driver.xml file for the tool
     513}
     514
     515# Write out the driver.xml file for the tool
     516itcl::body Rappture::Task::GetDriverFile {} {
    220517    global rapptureInfo
    221     set file [file join $rapptureInfo(cwd) "driver[pid].xml"]
    222     set status [catch {
    223         set fid [open $file w]
    224         puts $fid "<?xml version=\"1.0\"?>"
    225         puts $fid [$_xmlobj xml]
    226         close $fid
    227     } result]
    228 
    229     if {$uq_type != ""} {
    230         # Copy xml into a new file
    231         set tfile "template[pid].xml"
    232         set fid [open $tfile w]
    233         puts $fid "<?xml version=\"1.0\"?>"
    234         puts $fid [$_xmlobj xml]
    235         close $fid
    236 
    237         # Return a list of the UQ variables and their PDFs.
    238         # Also turns $tfile into a template file.
    239         set uq_varlist [lindex [$_xmlobj uq_get_vars $tfile] 0]
    240     }
    241 
    242 
    243     # execute the tool using the path from the tool description
    244     if {$status == 0} {
    245         set cmd [$_xmlobj get tool.command]
    246         regsub -all @tool $cmd $_installdir cmd
    247         set cmd [string trimleft $cmd " "]
    248 
    249         if { $cmd == "" } {
    250             puts stderr "cmd is empty"
    251             return [list 1 "Command is empty.\n\nThere is no command specified by\n\n <command>\n </command>\n\nin the tool.xml file."]
    252         }
    253 
    254         if {$uq_type == ""} {
    255             regsub -all @driver $cmd $file cmd
    256 
    257             switch -glob -- [resources -jobprotocol] {
    258                 "submit*" {
    259                     # if job_protocol is "submit", then use use submit command
    260                     set cmd "submit --local $cmd"
    261                 }
    262                 "mx" {
    263                     # metachory submission
    264                     set cmd "mx $cmd"
    265                 }
    266                 "exec" {
    267                     # default -- nothing special
    268                 }
     518    set fileName [file join $rapptureInfo(cwd) "driver[pid].xml"]
     519    if { [catch {
     520        set f [open $fileName w]
     521        puts $f "<?xml version=\"1.0\"?>"
     522        puts $f [$_xmlobj xml]
     523        close $f
     524    } errs] != 0 } {
     525        error "can't create driver file \"$fileName\": $errs"
     526    }
     527    return $fileName
     528}
     529
     530itcl::body Rappture::Task::GetCommand { } {
     531    set cmd [$_xmlobj get tool.command]
     532    regsub -all @tool $cmd $_installdir cmd
     533    set cmd [string trimleft $cmd " "]
     534    return $cmd
     535}
     536
     537itcl::body Rappture::Task::GetSimulationCommand { driverFile } {
     538    set cmd [GetCommand]
     539    if { $cmd == "" } {
     540        return ""
     541    }
     542    regsub -all @driver $cmd $driverFile cmd
     543
     544    switch -glob -- [resources -jobprotocol] {
     545        "submit*" {
     546            # if job_protocol is "submit", then use use submit command
     547            set cmd "submit --local $cmd"
     548        }
     549        "mx" {
     550            # metachory submission
     551            set cmd "mx $cmd"
     552        }
     553        "exec" {
     554            # default -- nothing special
     555        }
     556    }
     557    return $cmd
     558}
     559
     560itcl::body Rappture::Task::GetUQSimulationCommand { driverFile } {
     561    set cmd [GetCommand]
     562    if { $cmd == "" } {
     563        return ""
     564    }
     565    set paramsFile [GetParamsForUQ]
     566    set cmd [BuildSubmitCommand $cmd $_uq(tfile) $paramsFile]
     567    file delete -force puq
     568    return $cmd
     569}
     570
     571itcl::body Rappture::Task::GetUQTemplateFile {} {
     572    global rapptureInfo
     573    # Copy xml into a new file
     574    set templateFile "template[pid].xml"
     575    set f [open $templateFile w]
     576    puts $f "<?xml version=\"1.0\"?>"
     577    puts $f [$_xmlobj xml]
     578    close $f
     579
     580    # Return a list of the UQ variables and their PDFs.
     581    # Also turns $uq(tfile) into a template file.
     582    set _uq(varlist) [lindex [$_xmlobj uq_get_vars $templateFile] 0]
     583    set _uq(tfile) $templateFile
     584    return $templateFile
     585}
     586
     587itcl::body Rappture::Task::ExecuteSimulationCommand { cmd } {
     588
     589    set _job(runfile) ""
     590    set _job(success) 0
     591    set _job(exitcode) 0
     592
     593    # Step 1.  Write the command into the run file.
     594    $_xmlobj put tool.execute $cmd
     595
     596    Log run started
     597    Rappture::rusage mark
     598
     599    # Step 2.  Check if it is a special case "ECHO" command which always
     600    #          succeeds.
     601    if { [string compare -nocase -length 5 $cmd "ECHO "] == 0 } {
     602        set _job(stdout) [string range $cmd 5 end]
     603        set _job(success) 1
     604        set _job(exitcode) 0
     605        set _job(mesg) ""
     606        return 1;                       # Success
     607    }
     608
     609    # Step 3. Execute the command, collecting its stdout and stderr.
     610    catch {
     611        eval blt::bgexec [list [itcl::scope _job(control)]] \
     612            -keepnewline yes \
     613            -killsignal  SIGTERM \
     614            -onoutput    [list [itcl::code $this OnOutput]] \
     615            -output      [list [itcl::scope _job(stdout)]] \
     616            -error       [list [itcl::scope _job(stderr)]] \
     617            $cmd
     618    } result
     619
     620    # Step 4. Check the token and the exit code.
     621    set logmesg $result
     622    foreach { token _job(pid) _job(exitcode) mesg } $_job(control) break
     623    if { $token == "EXITED" } {
     624        if { $_job(exitcode) != 0 } {
     625            # This means that the program exited normally but returned a
     626            # non-zero exitcode.  Consider this an invalid result from the
     627            # program.  Append the stderr from the program to the message.
     628            if {$_job(exitcode) > 128} {
     629                set logmesg "Program signaled: signal was [GetSignal $_job(exitcode)]"
     630            } else {
     631                set logmesg "Program finished: non-zero exit code is $_job(exitcode)"
    269632            }
    270         } else {
    271             set params_file [_get_params $uq_varlist $uq_type $uq_args]
    272             set cmd [_build_submit_cmd $cmd $tfile $params_file]
    273             file delete -force puq
    274         }
    275 
    276         $_xmlobj put tool.execute $cmd
    277 
    278         # starting job...
    279         set _lastrun ""
    280         _log run started
    281         Rappture::rusage mark
    282 
    283         if {0 == [string compare -nocase -length 5 $cmd "ECHO "] } {
    284             set status 0;
    285             set job(output) [string range $cmd 5 end]
    286         } else {
    287             set status [catch {
    288                 set ::Rappture::Task::job(control) ""
    289                 eval blt::bgexec \
    290                 ::Rappture::Task::job(control) \
    291                 -keepnewline yes \
    292                 -killsignal SIGTERM \
    293                 -onoutput [list [itcl::code $this _output]] \
    294                 -output ::Rappture::Task::job(output) \
    295                 -error ::Rappture::Task::job(error) \
    296                 $cmd
    297             } result]
    298 
    299             if { $status != 0 } {
    300                 # We're here because the exec-ed program failed
    301                 set logmesg $result
    302                 if { $::Rappture::Task::job(control) ne "" } {
    303                     foreach { token pid code mesg } \
    304                     $::Rappture::Task::job(control) break
    305                     if { $token == "EXITED" } {
    306                        # This means that the program exited normally but
    307                        # returned a non-zero exitcode.  Consider this an
    308                        # invalid result from the program.  Append the stderr
    309                        # from the program to the message.
    310                        if {$code > 128} {
    311                           set logmesg "Program signaled: signal was [GetSignal $code]"
    312                        } else {
    313                           set logmesg "Program finished: exit code is $code"
    314                        }
    315                        set result "$logmesg\n\n$::Rappture::Task::job(error)"
    316                     } elseif { $token == "abort" }  {
    317                         # The user pressed the abort button.
    318                         set logmesg "Program terminated by user."
    319                         set result "$logmesg\n\n$::Rappture::Task::job(output)"
    320                     } else {
    321                         # Abnormal termination
    322                         set logmesg "Abnormal program termination: $mesg"
    323                         set result "$logmesg\n\n$::Rappture::Task::job(output)"
    324                     }
    325                 }
    326                 _log run failed [list $logmesg]
    327                 return [list $status $result]
    328             }
    329         }
    330         # ...job is finished
    331         array set times [Rappture::rusage measure]
    332 
    333         if {[resources -jobprotocol] ne "submit"} {
    334             set id [$_xmlobj get tool.id]
    335             set vers [$_xmlobj get tool.version.application.revision]
    336             set simulation simulation
    337             if { $id ne "" && $vers ne "" } {
    338                 set pid [pid]
    339                 set simulation ${pid}_${id}_r${vers}
    340             }
    341 
    342             # need to save job info? then invoke the callback
    343             if {[string length $jobstats] > 0} {
    344                 uplevel #0 $jobstats [list job [incr jobnum] \
    345                 event $simulation start $times(start) \
    346                 walltime $times(walltime) cputime $times(cputime) \
    347                 status $status]
    348             }
    349 
    350             #
    351             # Scan through stderr channel and look for statements that
    352             # represent grid jobs that were executed.  The statements
    353             # look like this:
    354             #
    355             # MiddlewareTime: job=1 event=simulation start=3.001094 ...
    356             #
    357             set subjobs 0
    358             while {[regexp -indices {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)} $job(error) match]} {
    359                 foreach {p0 p1} $match break
    360                 if {[string index $job(error) $p0] == "\n"} { incr p0 }
    361 
    362                 catch {unset data}
    363                 array set data {
    364                     job 1
    365                     event simulation
    366                     start 0
    367                     walltime 0
    368                     cputime 0
    369                     status 0
    370                 }
    371                 foreach arg [lrange [string range $job(error) $p0 $p1] 1 end] {
    372                     foreach {key val} [split $arg =] break
    373                     set data($key) $val
    374                 }
    375                 set data(job) [expr {$jobnum+$data(job)}]
    376                 set data(event) "subsimulation"
    377                 set data(start) [expr {$times(start)+$data(start)}]
    378 
    379                 set details ""
    380                 foreach key {job event start walltime cputime status} {
    381                     # add required keys in a particular order
    382                     lappend details $key $data($key)
    383                     unset data($key)
    384                 }
    385                 foreach key [array names data] {
    386                     # add anything else that the client gave -- venue, etc.
    387                     lappend details $key $data($key)
    388                 }
    389 
    390                 if {[string length $jobstats] > 0} {
    391                     uplevel #0 $jobstats $details
    392                 }
    393 
    394                 incr subjobs
    395 
    396                 # done -- remove this statement
    397                 set job(error) [string replace $job(error) $p0 $p1]
    398             }
    399             incr jobnum $subjobs
    400         }
    401 
     633            set _job(mesg) "$logmesg\n\n$_job(stderr)"
     634            Log run failed [list $logmesg]
     635            return 0;                   # Fail.
     636        }
     637        # Successful program termination with exit code of 0.
     638    } elseif { $token == "abort" }  {
     639        # The user pressed the abort button.
     640       
     641        set logmesg "Program terminated by user."
     642        Log run failed [list $logmesg]
     643        set _job(mesg) "$logmesg\n\n$_job(stdout)"
     644        return 0;                       # Fail
    402645    } else {
    403         set job(error) "$result\n$errorInfo"
    404     }
    405     if {$status == 0} {
    406         # file delete -force -- $file
    407     }
    408 
    409     # see if the job was aborted
    410     if {[regexp {^KILLED} $job(control)]} {
    411         _log run aborted
    412         return [list 0 "ABORT"]
    413     }
    414 
     646        # Abnormal termination
     647       
     648        set logmesg "Abnormal program termination:"
     649        Log run failed [list $logmesg]
     650        set _job(mesg) "$logmesg\n\n$_job(stdout)"
     651        return 0;                       # Fail
     652    }
     653    if { $_uq(type) != "" } {
     654        CollectUQResults
     655    }
     656
     657    # Step 5. Look in stdout for the name of the run file.
     658    set pattern {=RAPPTURE-RUN=>([^\n]+)}
     659    if {![regexp $pattern $_job(stdout) match fileName]} {
     660        set _job(mesg) "Can't find result file in output.\n"
     661        append _job(mesg) "Did you call Rappture::result in your simulator?"
     662        return 0;                       # Fail
     663    }
     664    set _job(runfile) $fileName
     665    set _job(success) 1
     666    set _job(mesg) $_job(stdout)
     667    return 1;                           # Success
     668}
     669
     670itcl::body Rappture::Task::LogSimulationUsage {} {
     671    array set times [Rappture::rusage measure]
     672
     673    set toolId     [$_xmlobj get tool.id]
     674    set toolVers   [$_xmlobj get tool.version.application.revision]
     675    set simulation "simulation"
     676    if { $toolId ne "" && $toolVers ne "" } {
     677        set simulation "[pid]_${toolId}_r${toolVers}"
     678    }
     679   
     680    # Need to save job info? then invoke the callback
     681    if { [string length $jobstats] > 0} {
     682        lappend args \
     683            "job"      [incr jobnum] \
     684            "event"    $simulation \
     685            "start"    $times(start) \
     686            "walltime" $times(walltime) \
     687            "cputime"  $times(cputime) \
     688            "status"   $_job(exitcode)
     689        uplevel #0 $jobstats $args
     690    }
     691       
    415692    #
    416     # If successful, return the output, which should include
    417     # a reference to the run.xml file containing results.
     693    # Scan through stderr channel and look for statements that
     694    # represent grid jobs that were executed.  The statements look
     695    # like this:
    418696    #
    419 
    420     if {$status == 0} {
    421         set result [string trim $job(output)]
    422 
    423         if {$uq_type != ""} {
    424             # UQ. Collect data from all jobs and put it in one xml run file.
    425             file delete -force -- run_uq.xml
    426             if {[catch {exec puq.sh analyze puq_[pid].hdf5} res]} {
    427                 set fp [open "uq_debug.err" r]
    428                 set rdata [read $fp]
    429                 close $fp
    430                 puts "PUQ analysis failed: $res\n$rdata"
    431                 error "UQ analysis failed: $res\n$rdata"
    432             } else {
    433                 append result "\n" $res
    434             }
    435         }
    436         if {[regexp {=RAPPTURE-RUN=>([^\n]+)} $result match file]} {
    437             set _lastrun $file
    438 
    439             set status [catch {Rappture::library $file} result]
    440             if {$status == 0} {
    441                 # add cputime info to run.xml file
    442                 $result put output.walltime $times(walltime)
    443                 $result put output.cputime $times(cputime)
    444                 if {[info exists env(SESSION)]} {
    445                     $result put output.session $env(SESSION)
    446                 }
    447             } else {
    448                 global errorInfo
    449                 set result "$result\n$errorInfo"
    450             }
    451 
    452             file delete -force -- $file
    453         } else {
    454             set status 1
    455             set result "Can't find result file in output.\nDid you call Rappture
    456 ::result in your simulator?"
    457         }
    458     } elseif {$job(output) ne "" || $job(error) ne ""} {
    459         set result [string trim "$job(output)\n$job(error)"]
    460     }
    461 
    462     # log final status for the run
    463     if {$status == 0} {
    464         _log run finished
     697    # MiddlewareTime: job=1 event=simulation start=3.001094 ...
     698    #
     699   
     700    set subjobs 0
     701    set pattern {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)}
     702    while { [regexp -indices $pattern $_job(stderr) match] } {
     703        foreach {p0 p1} $match break
     704        if { [string index $_job(stderr) $p0] == "\n" } {
     705            incr p0
     706        }
     707        array unset data
     708        array set data {
     709            job 1
     710            event simulation
     711            start 0
     712            walltime 0
     713            cputime 0
     714            status 0
     715        }
     716        foreach arg [lrange [string range $_job(stderr) $p0 $p1] 1 end] {
     717            foreach {key val} [split $arg =] break
     718            set data($key) $val
     719        }
     720        set data(job)   [expr { $jobnum + $data(job) }]
     721        set data(event) "subsimulation"
     722        set data(start) [expr { $times(start) + $data(start) }]
     723       
     724        set details ""
     725        foreach key {job event start walltime cputime status} {
     726            # Add required keys in a particular order
     727            lappend details $key $data($key)
     728            unset data($key)
     729        }
     730        foreach key [array names data] {
     731            # Add anything else that the client gave -- venue, etc.
     732            lappend details $key $data($key)
     733        }
     734       
     735        if {[string length $jobstats] > 0} {
     736            uplevel #0 $jobstats $details
     737        }
     738       
     739        incr subjobs
     740       
     741        # Done -- remove this statement
     742        set _job(stderr) [string replace $_job(stderr) $p0 $p1]
     743    }
     744    incr jobnum $subjobs
     745
     746    # Add cputime info to run.xml file
     747    if { [catch {
     748        Rappture::library $_job(runfile)
     749    } xmlobj] != 0 } {
     750        error "Can't create rappture library: $xmlobj"
     751    }
     752    $xmlobj put output.walltime $times(walltime)
     753    $xmlobj put output.cputime $times(cputime)
     754    global env
     755    if {[info exists env(SESSION)]} {
     756        $xmlobj put output.session $env(SESSION)
     757    }
     758    set _job(xmlobj) $xmlobj
     759}
     760
     761itcl::body Rappture::Task::LogSubmittedSimulationUsage {} {
     762    array set times [Rappture::rusage measure]
     763
     764    set toolId     [$_xmlobj get tool.id]
     765    set toolVers   [$_xmlobj get tool.version.application.revision]
     766    set simulation "simulation"
     767    if { $toolId ne "" && $toolVers ne "" } {
     768        set simulation "[pid]_${toolId}_r${toolVers}"
     769    }
     770   
     771    # Need to save job info? then invoke the callback
     772    if { [string length $jobstats] > 0} {
     773        lappend args \
     774            "job"      [incr jobnum] \
     775            "event"    $simulation \
     776            "start"    $times(start) \
     777            "walltime" $times(walltime) \
     778            "cputime"  $times(cputime) \
     779            "status"   $_job(exitcode)
     780        uplevel #0 $jobstats $args
     781    }
     782       
     783    #
     784    # Scan through stderr channel and look for statements that
     785    # represent grid jobs that were executed.  The statements look
     786    # like this:
     787    #
     788    # MiddlewareTime: job=1 event=simulation start=3.001094 ...
     789    #
     790   
     791    set subjobs 0
     792    set pattern {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)}
     793    while { [regexp -indices $pattern $_job(stderr) match] } {
     794        foreach {p0 p1} $match break
     795        if { [string index $_job(stderr) $p0] == "\n" } {
     796            incr p0
     797        }
     798        array unset data
     799        array set data {
     800            job 1
     801            event simulation
     802            start 0
     803            walltime 0
     804            cputime 0
     805            status 0
     806        }
     807        foreach arg [lrange [string range $_job(stderr) $p0 $p1] 1 end] {
     808            foreach {key val} [split $arg =] break
     809            set data($key) $val
     810        }
     811        set data(job)   [expr { $jobnum + $data(job) }]
     812        set data(event) "subsimulation"
     813        set data(start) [expr { $times(start) + $data(start) }]
     814       
     815        set details ""
     816        foreach key {job event start walltime cputime status} {
     817            # Add required keys in a particular order
     818            lappend details $key $data($key)
     819            unset data($key)
     820        }
     821        foreach key [array names data] {
     822            # Add anything else that the client gave -- venue, etc.
     823            lappend details $key $data($key)
     824        }
     825       
     826        if {[string length $jobstats] > 0} {
     827            uplevel #0 $jobstats $details
     828        }
     829       
     830        incr subjobs
     831       
     832        # Done -- remove this statement
     833        set _job(stderr) [string replace $_job(stderr) $p0 $p1]
     834    }
     835    incr jobnum $subjobs
     836
     837    # Add cputime info to run.xml file
     838    if { [catch {
     839        Rappture::library $_job(runfile)
     840    } xmlobj] != 0 } {
     841        error "Can't create rappture library: $xmlobj"
     842    }
     843    set _job(xmlobj) $xmlobj
     844}
     845
     846itcl::body Rappture::Task::LogCachedSimulationUsage {} {
     847    if { [catch {
     848        Rappture::library $_job(runfile)
     849    } xmlobj] != 0 } {
     850        error "Can't create rappture library: $xmlobj"
     851    }
     852    # Get the session from runfile
     853    set session [$xmlobj get "output.session"]
     854    if { [catch {exec submit --cache $session} result] != 0 } {
     855       puts stderr "submit --cache failed: $result"
     856    }
     857    set _job(xmlobj) $xmlobj
     858}
     859
     860
     861itcl::body Rappture::Task::CheckForCachedRunFile { driverFile } {
     862
     863    # Read the driver file and collect its contents as the query.
     864    set url http://$_resources(-cachehosts)/cache/request
     865    set f [open $driverFile "r"]
     866    set query [read $f]
     867    close $f
     868
     869    # Make the query
     870    if { [catch {
     871        http::geturl $url -query $query -timeout 6000 -binary yes
     872    } token] != 0 } {
     873        puts stderr "error performing cache query: token=$token"
     874        return 0
     875    }
     876    # If the code isn't 200, we'll assume it's a cache miss.
     877    if { [http::ncode $token] != 200} {
     878        return 0
     879    }
     880    # Get contents of the run file.
     881    set contents [http::data $token]
     882    if { $contents == "" } {
     883        return 0
     884    }
     885
     886    # Create a new run.xml file and write the results into it.
     887    set secs [clock seconds]
     888    set millisecs [expr [clock clicks -milliseconds] % 1000]
     889    set timestamp [format %d%03d%03d $secs $millisecs 0]
     890
     891    global rapptureInfo
     892    set fileName [file join $rapptureInfo(cwd) "run${timestamp}.xml"]
     893    set f [open $fileName "w"]
     894    puts $f $contents
     895    close $f
     896    set _job(runfile) $fileName
     897    set _job(success) 1
     898    set _job(stderr) "Loading cached results\n"
     899    OnOutput "Loading cached results\n"
     900    update
     901    return 1
     902}
     903
     904itcl::body Rappture::Task::GetUQErrors {} {
     905    set contents {}
     906    if { [file exists "uq_debug.err"] } {
     907        set f [open "uq_debug.err" r]
     908        set contents [read $f]
     909        close $f
     910    }
     911    return $contents
     912}
     913
     914# UQ. Collect data from all jobs and put it in one xml run file.
     915itcl::body Rappture::Task::CollectUQResults {} {
     916    file delete -force -- "run_uq.xml"
     917    set hdfFile puq_[pid].hdf5
     918    if { [catch {
     919        exec puq.sh analyze $hdfFile
     920    } results] != 0 } {
     921        error "UQ analysis failed: $results\n[GetUQErrors]"
    465922    } else {
    466         _log run failed [list $result]
    467     }
    468 
    469     return [list $status $result]
    470 }
    471 
    472 # ----------------------------------------------------------------------
    473 #  Turn the command string from tool.xml into the proper syntax to use
    474 #  with a submit parameter sweep with a temlate file.  Proper quoting
    475 # of the template file is necessary to prevent submit from being too smart
    476 # and converting it to a full pathname.
    477 # ----------------------------------------------------------------------
    478 itcl::body Rappture::Task::_build_submit_cmd {cmd tfile params_file} {
    479     set quote_next 0
    480     set newcmd "submit --progress submit --runName=puq -l -i @:$tfile -d $params_file"
    481     set cmds [split $cmd " "]
    482     for {set i 0} {$i < [llength $cmds]} {incr i} {
    483         set arg [lindex $cmds $i]
    484         if {$quote_next == 1} {
    485             set nc [string range $arg 0 0]
    486             if {$nc != "\""} {
    487                 set arg "\"\\\"$arg\\\"\""
    488             }
    489         }
    490         if {$arg == "--eval"} {
    491             set quote_next 1
    492         } else {
    493             set quote_next 0
    494         }
    495         if {$arg == "@driver"} {
    496             set arg "\"\\\"$tfile\\\"\""
    497         }
    498         append newcmd " " $arg
    499     }
    500     regsub -all @driver $newcmd $tfile newcmd
    501     return $newcmd
    502 }
    503 
    504 # ----------------------------------------------------------------------
    505 # USAGE: _mkdir <directory>
    506 #
    507 # Used internally to create the <directory> in the file system.
    508 # The parent directory is also created, as needed.
    509 # ----------------------------------------------------------------------
    510 itcl::body Rappture::Task::_mkdir {dir} {
    511     set parent [file dirname $dir]
    512     if {$parent ne "." && $parent ne "/"} {
    513         if {![file exists $parent]} {
    514             _mkdir $parent
    515         }
    516     }
    517     file mkdir $dir
    518 }
    519 
    520 
    521 # ----------------------------------------------------------------------
    522 # USAGE: abort
    523 #
    524 # Clients use this during a "run" to abort the current job.
    525 # Kills the job and forces the "run" method to return.
    526 # ----------------------------------------------------------------------
    527 itcl::body Rappture::Task::abort {} {
    528     _log run abort
    529     set job(control) "abort"
    530 }
    531 
    532 # ----------------------------------------------------------------------
    533 # USAGE: reset
    534 #
    535 # Resets all input values to their defaults.  Sometimes used just
    536 # before a run to reset to a clean state.
    537 # ----------------------------------------------------------------------
    538 itcl::body Rappture::Task::reset {} {
    539     $_xmlobj copy "" from $_origxml ""
    540     foreach path [Rappture::entities -as path $_xmlobj input] {
    541         if {[$_xmlobj element -as type $path.default] ne ""} {
    542             set defval [$_xmlobj get $path.default]
    543             $_xmlobj put $path.current $defval
    544         }
    545     }
    546 }
    547 
    548 # ----------------------------------------------------------------------
    549 # USAGE: xml <subcommand> ?<arg> <arg> ...?
    550 # USAGE: xml object
    551 #
    552 # Used by clients to manipulate the underlying XML data for this
    553 # tool.  The <subcommand> can be any operation supported by a
    554 # Rappture::library object.  Clients can also request the XML object
    555 # directly by using the "object" subcommand.
    556 # ----------------------------------------------------------------------
    557 itcl::body Rappture::Task::xml {args} {
    558     if {"object" == $args} {
    559         return $_xmlobj
    560     }
    561     return [eval $_xmlobj $args]
    562 }
    563 
    564 # ----------------------------------------------------------------------
    565 # USAGE: save <xmlobj> ?<filename>?
    566 #
    567 # Used by clients to save the contents of an <xmlobj> representing
    568 # a run out to the given file.  If <filename> is not specified, then
    569 # it uses the -resultsdir and other settings to do what Rappture
    570 # would normally do with the output.
    571 # ----------------------------------------------------------------------
    572 itcl::body Rappture::Task::save {xmlobj {filename ""}} {
    573     if {$filename eq ""} {
    574         # if there's a results_directory defined in the resources
    575         # file, then move the run.xml file there for storage
    576         set rdir ""
    577         if {$resultdir eq "@default"} {
    578             if {[info exists _resources(-resultdir)]} {
    579                 set rdir $_resources(-resultdir)
    580             } else {
    581                 global rapptureInfo
    582                 set rdir $rapptureInfo(cwd)
    583             }
    584         } elseif {$resultdir ne ""} {
    585             set rdir $resultdir
    586         }
    587 
    588         # use the runfile name generated by the last run
    589         if {$_lastrun ne ""} {
    590             set filename [file join $rdir $_lastrun]
    591         } else {
    592             set filename [file join $rdir run.xml]
    593         }
    594     }
    595 
    596     # add any last-minute metadata
    597     $xmlobj put output.time [clock format [clock seconds]]
    598 
    599     $xmlobj put tool.version.rappture.version $::Rappture::version
    600     $xmlobj put tool.version.rappture.revision $::Rappture::build
    601 
    602     if {[info exists ::tcl_platform(user)]} {
    603         $xmlobj put output.user $::tcl_platform(user)
    604     }
    605 
    606     # save the output
    607     set rdir [file dirname $filename]
    608     if {![file exists $rdir]} {
    609         _mkdir $rdir
    610     }
    611 
    612     set fid [open $filename w]
    613     puts $fid "<?xml version=\"1.0\"?>"
    614     puts $fid [$xmlobj xml]
    615     close $fid
    616 
    617     _log output saved in $filename
    618 }
    619 
    620 # ----------------------------------------------------------------------
    621 # USAGE: _output <data>
    622 #
    623 # Used internally to send each bit of output <data> coming from the
    624 # tool onto the caller, so the user can see progress.
    625 # ----------------------------------------------------------------------
    626 itcl::body Rappture::Task::_output {data} {
    627     if {[string length $_outputcb] > 0} {
    628         uplevel #0 $_outputcb [list $data]
    629     }
    630 }
    631 
    632 # ----------------------------------------------------------------------
    633 # USAGE: _log <cmd> <arg> <arg> ...
    634 #
    635 # Used internally to log interesting events during the run.  If the
    636 # -logger option is set (to Rappture::Logger::log, or something like
    637 # that), then the arguments to this method are passed along to the
    638 # logger and written out to a log file.  Logging is off by default,
    639 # so this method does nothing unless -logger is set.
    640 # ----------------------------------------------------------------------
    641 itcl::body Rappture::Task::_log {args} {
    642     if {[string length $logger] > 0} {
    643         uplevel #0 $logger [list $args]
    644     }
    645 }
    646 
    647 # ----------------------------------------------------------------------
    648 # USAGE: MiddlewareTime <key> <value> ...
    649 #
    650 # Used as the default method for reporting job status information.
    651 # Implements the old HUBzero method of reporting job status info to
    652 # stderr, which can then be picked up by the tool session container.
    653 # Most tools use the "submit" command, which talks directly to a
    654 # database to log job information, so this isn't really needed.  But
    655 # it doesn't hurt to have this and it can be useful in some cases.
    656 # ----------------------------------------------------------------------
    657 itcl::body Rappture::Task::MiddlewareTime {args} {
    658     set line "MiddlewareTime:"
    659     foreach {key val} $args {
    660         append line " $key=$val"
    661     }
    662     puts stderr $line
    663 }
    664 
    665 
    666 #
    667 # Send the list of parameters to a python program so it can call PUQ
    668 # and get a CSV file containing the parameter values to use for the runs.
    669 itcl::body Rappture::Task::_get_params {varlist uq_type uq_args} {
    670     set pid [pid]
    671     # puts "puq.sh get_params $pid $varlist $uq_type $uq_args"
    672     if { [catch {
    673         exec puq.sh get_params $pid $varlist $uq_type $uq_args
    674     } errs] != 0 } {
    675         set contents {}
    676         if { [file exists "uq_debug.err"] } {
    677             set fp [open "uq_debug.err" r]
    678             set contents [read $fp]
    679             close $fp
    680         }
    681         puts "get_params.py failed: $errs\n$contents"
    682         error "get_params.py failed: $errs\n$contents"
    683     }
    684     return params[pid].csv
    685 }
     923        set _job(stdout) $results
     924    }
     925}
  • trunk/src/core/RpLibrary.cc

    r5673 r6473  
    17911791
    17921792    // check for binary data
    1793     // FIXME: I've already appended a NUL-byte of this assuming that
     1793    // FIXME: I've already appended a NUL-byte to this assuming that
    17941794    //        it's a ASCII string. This test must come before.
    17951795    if (Rappture::encoding::isBinary(value.c_str(), value.length())) {
     
    20472047        return *this;
    20482048    }
    2049     if (compress == RPLIB_COMPRESS) {
     2049    if ((compress == RPLIB_COMPRESS) ||
     2050        (Rappture::encoding::isBinary(fileBuf.bytes(), fileBuf.size()))) {     
    20502051        putData(path, fileBuf.bytes(), fileBuf.size(), append);
    20512052    } else {
Note: See TracChangeset for help on using the changeset viewer.