5.5   The Browser Presentation

Most information in the Tornado browser is presented in the form of windows. For an introduction to the browser, see the Tornado User's Guide: Browser. The following sections step through the process of creating display windows in the browser and using them to display the data extracted in 5.3 Extracting Information from a Structure.

5.5.1   UNIX Implementation

Creating the Hierarchy Window Display

In the UNIX browser, most information is presented in hierarchy windows. These are windows that display information in a hierarchical or list format. The windows are defined using UI Tcl; for more information, see the on-line help.

Use the core routines described in 5.3 Extracting Information from a Structure to produce the browser panel shown in Figure 5-3. The steps in creating the new window display are:

Defining a New Window

Hierarchy windows are created with the Tcl procedure hierarchyCreate. The -destroy option calls the routine ifDestroyed if the user dismisses the new window from the window manager (see Installing the New Browser Panel). The -change option causes any values that have been updated between one evaluation and the next to be highlighted. (In Figure 5-3 this occurred with packets received, packets sent, and errors.) The new window is called Network Interfaces. The complete command to create the window is:

hierarchyCreate -destroy ifDestroyed -change "Network Interfaces"
Defining the Data Layout

The next step is to use hierarchySetStructure to describe the structure of the hierarchy that the window will display. For a detailed explanation of this and other procedures, see the on-line help.

The format of data for a hierarchy window mirrors that of the structure string. Arrays of objects are surrounded by braces. Simple objects follow one another separated by spaces; nested objects are placed in braces. The output of the procedure is a nested list in the standard Tcl format.

The first six elements of the display (name through mtu) are simple objects, represented in the structure description by their names:

name flags addr destaddr metric mtu

Each nested element consists of a bracketed list which itself has two elements, the first being the name of the header item (the folder) and the second the sublist of subordinate items:

{packets {received sent}} 
{errors {input output collisions}}

To arrange for an array of structures (in this example, a listing of data for all network interfaces), create a list whose first element is the special name + and whose second element is the substructure, enclosed in braces. This bracketed list becomes the second argument for the hierarchySetStructure procedure; the first argument is the window name.

hierarchySetStructure "Network Interfaces" \ 
    {+ {name flags addr destaddr metric mtu \ 
     {packets {received sent}} \ 
     {errors {input output collisions}}}}

This code must be in the browser initialization file so that the browser creates this hierarchy when it starts (host/resource/tcl/app-config/Browser/01NetBrws.tcl).

Generating the Data

The next step is to create a procedure that arranges the data from the network information-gathering routines (described in 5.3 Extracting Information from a Structure) into a form suitable for the hierarchySetValues procedure. The format of data for a hierarchy mirrors that of the structure string. For the window shown in Figure 5-3, the value string used to fill the data slots is:

{{ul0 {(0xf1) UP POINT-TO-POINT RUNNING } 127.0.1.7 147.11.80.6 \ 
    0 1536 {459 922} {0 0 0}} \ 
{lo0 {(0x69) UP LOOPBACK RUNNING ARP } 127.0.0.1 0.0.0.0 \ 
    0 4096 {3 3} {0 0 0}}}

Create a procedure that generates such a list (including formatting braces) from the data returned by the gathering routines. The Tcl lappend procedure serves this purpose by automatically adding braces when necessary (as when the new list element contains embedded spaces). First consider the data for just one interface, whose address is presumed to be in the variable netIf. Initialize a variable, thisIf, to the empty string, and then append the items called for one by one.

set thisIf "" 
set ifInfo [netIfInfo $netIf] 
 
lappend thisIf [format "%s%d" [lindex $ifInfo 0] \ 
    [lindex $ifInfo 1]] 
lappend thisIf [format "(%#x) %s" \ 
    [lindex $ifInfo 2] [ifFlagsFormat [lindex $ifInfo 2]]] 
 
set ifAddrList [netIfAddrList $netIf] 
lappend thisIf [ifAddrFormat [lrange $ifAddrList 0 3]] 
lappend thisIf [ifAddrFormat [lrange $ifAddrList 4 7]] 
# metric 
lappend thisIf [lindex $ifInfo 4] 
# mtu 
lappend thisIf [expr [lindex $ifInfo 3]] 
# add packet arrays 
lappend thisIf [list [expr [lindex $ifInfo 5]] \ 
    [expr [lindex $ifInfo 7]]] 
# add error/collision counts 
lappend thisIf [list [expr [lindex $ifInfo 6]] \ 
    [expr [lindex $ifInfo 8]] \ 
    [expr [lindex $ifInfo 9]]]

To complete the data collection for the window, you need only wrap this code in a loop which queries all network interfaces and accumulates the list generated by the above code into a "list of lists." Note how the nesting of the lappend procedure simplifies the creation of nested lists.

proc getAllIf {} { 
    set allIf "" 
    foreach netIf [netIfList] { 
        # code fragment above 
        lappend allIf $thisIf     
    } 
    return $allIf 
}

This makes it possible to generate the data by calling hierarchySetValues. The output will be in a form ready to be displayed as in Figure 5-3:

hierarchySetValues "Network Interfaces" [getAllIf]

Installing the New Browser Panel

Two steps are required to add this panel to the browser. First, create a toolbar button to request the display of the panel. Second, insert the panel value-computation routine into the browser main loop so that the information is updated regularly.

Add a toolbar button containing the string "netif" to the browser toolbar1 , with the following command:

toolBarItemCreate netif button {ifPost}

The Tcl procedure ifPost uses a global variable to keep track of whether the network interface browser is posted or not.

set netIfPosted 0  
proc ifPost {} { 
    global netIfPosted 
    hierarchyPost "Network Interfaces" 
    set netIfPosted 1 
    ifBrowse 
}

When you created the hierarchy window (in Defining a New Window), you indicated you wanted to call the procedure ifDestroyed when the window manager dismisses the interface browser. The purpose of ifDestroyed is to update the state variable.

proc ifDestroyed {name} { 
    global netIfPosted 
    set netIfPosted 0 
}

You want to update the network interface panel each time the browser updates other windows, either because the user presses the immediate update button or because the browser is in continuous update mode and the update interval elapses. To do this, call browserAuxProcAdd with the name of a procedure to invoke each time the browser is in its update cycle:

browserAuxProcAdd ifBrowse

This procedure, ifBrowse, checks the status of the global variable to see if the panel has been posted. If so, it collects the data and installs it in the hierarchy window. This completes the process of collecting data and displaying it in the browser.

proc ifBrowse {} { 
    global netIfPosted  
    if {! $netIfPosted} return 
    hierarchySetValues "Network Interfaces" $allIf 
}

The browser procedures are combined in a file in installDir/host/resource/tcl/app-config/Browser/01NetBrws.tcl, which also sources the data extraction routines developed earlier.

Example 5-3:  01NetBrws.tcl

This file should be stored in the installDir/host/resource/tcl/app-config/Browser directory.

# source the data extraction and formatting routines 
 
source [wtxPath host resource tcl]net-core.tcl
# create the hierarchy window to display the network interface info hierarchyCreate -destroy ifDestroyed -change "Network Interfaces"
# describe the structure to be displayed in the window hierarchySetStructure "Network Interfaces" \ {+ {name flags addr destaddr metric mtu \ {packets {received sent}}\ {errors {input output collision}}}}
# track whether the window is posted or not set netIfPosted 0 proc ifPost {} { global netIfPosted hierarchyPost "Network Interfaces" set netIfPosted 1 ifBrowse }
# update the state variable if we close the window proc ifDestroyed {name} { global netIfPosted set netIfPosted 0 }
# loop through and collect the data for all interfaces proc getAllIf {} { set allIf "" foreach netIf [netIfList] { set thisIf "" set ifInfo [netIfInfo $netIf]
lappend thisIf [format "%s%d" [lindex $ifInfo 0] \ [lindex $ifInfo 1]] lappend thisIf [format "(%#x) %s" \ [lindex $ifInfo 2] [ifFlagsFormat [lindex $ifInfo 2]]]
set ifAddrList [netIfAddrList $netIf] lappend thisIf [ifAddrFormat [lrange $ifAddrList 0 3]] lappend thisIf [ifAddrFormat [lrange $ifAddrList 4 7]] # metric lappend thisIf [lindex $ifInfo 4] # mtu lappend thisIf [expr [lindex $ifInfo 3]] # add packet arrays lappend thisIf [list [expr [lindex $ifInfo 5]] \ [expr [lindex $ifInfo 7]]] # add error/collision counts lappend thisIf [list [expr [lindex $ifInfo 6]] \ [expr [lindex $ifInfo 8]] \ [expr [lindex $ifInfo 9]]] lappend allIf $thisIf } return $allIf }
# see if the window has been posted; if so, collect and display all data proc ifBrowse {} { global netIfPosted if {! $netIfPosted} return hierarchySetValues "Network Interfaces" [getAllIf] }
# create the button on the toolbar toolBarItemCreate netif button {ifPost}
# update the display when other browser displays are updated browserAuxProcAdd ifBrowse

5.5.2   Windows Implementation

The following sample Tcl application extends the Tornado browser by adding a new page to display network interface information. Add extension files following these guidelines:

  1. Place all files you want auto-sourced by the browser into the subdirectory installDir\resource\tcl\app-config\browser and name them filename.win32.tcl. You must source the file that contains the host-independent data extraction and formatting routines:

source [wtxPath host resource tcl]net-core.tcl

  1. Append a page title string to the browser global pageList variable:

lappend pageList "Network Information"
This makes the Windows browser aware of the new browser screen.

  1. Create a hook routine to be called by the browser when your page description string is selected from the browser page list: invokeNetworkInformation.

proc invokeNetworkInformation {} { 
        global currentBrowserProc 
        global autoResize 
 
        allControlsDestroy Browser 
 
        setupNetworkInfoPage 
 
        set currentBrowserProc refreshNetworkInfoPage 
        if {$autoResize} { 
        windowSizeCallbackSet Browser "adjustNetworkInfoPage" 
        } 
}
The name has the form:

invokeDescriptionStringW/oSpaces 
where the description string is the page title sting appended to pageList. This procedure destroys all the previous browser controls and calls procedures to initialize the page, refresh the data, and adjust the tree control when the user resizes the browser window.

  1. Perform initial setup for the browser page. This step creates a tree control, initializes its structure, gets the required network information, and inserts it into the tree control.

proc setupNetworkInfoPage {} { 
    global autoResize
beginWaitCursor
if {$autoResize} { set clientSz [windowClientSizeGet Browser] set maxXExtent [expr [lindex $clientSz 0] - 14] set maxYExtent [expr [lindex $clientSz 1] - 14] } { set maxXExtent 300 set maxYExtent 400 }
controlCreate Browser [list hierarchy -name networkInfo \ -x 7 -y 7 -w $maxXExtent -h $maxYExtent] controlStructureSet Browser.networkInfo \ "+ {name flags addr destaddr metric mtu \ {packets {received sent}}\ {errors {input output collision}}}"
refreshNetworkInfoPage endWaitCursor }

  1. The following callback adjusts the tree control so that it fits properly when the user resizes the browser window.

proc adjustNetworkInfoPage {} { 
    global autoResize 
 
    if [controlExists Browser.networkInfo] { 
        set clientSz [windowClientSizeGet Browser] 
 
        if {$autoResize} { 
            set maxXExtent [expr [lindex $clientSz 0] - 14] 
            set maxYExtent [expr [lindex $clientSz 1] - 14] 
        } { 
            set maxXExtent 300 
            set maxYExtent 160 
        } 
        controlPositionSet Browser.networkInfo 7 25 
        controlSizeSet Browser.networkInfo $maxXExtent \ 
                [expr $maxYExtent - 18] 
    } 
}

  1. The following callback refreshes the data displayed by the tree control. getAllIf is the wrapper for the shared code in net-core.tcl. It loops through and collects the data for all network interfaces. (For the complete procedure, see Example 5-4.)

proc refreshNetworkInfoPage {} { 
        controlValuesSet Browser.networkInfo [getAllIf] 
}

Example 5-4 gives the complete code to install the sample Tcl application (developed in Generating the Data) in the browser on a Windows host.

Example 5-4:  01NetBrws.win32.tcl

This file should be stored in the installDir\host\resource\tcl\app-config\browser directory.

########################################################################## 
# 01NetBrws.win32.tcl - sample UITcl code illustrating user extension of the 
# Tornado Browser. 
# 
# Copyright 1994-1997 Wind River Systems, Inc. 
# 
# This sample Tcl application extends the Tornado browser by adding a new 
# page to display network interface information. 
# 
######################################################################### 
# Do not source this file if the platform is not win32 
 
if {![string match *win32* [wtxHostType]]} return 
 
# source the host-independent data extraction and formatting routines 
 
source [wtxPath host resource tcl]net-core.tcl 
 
# hook our page into the browser 
 
lappend pageList "Network Information" 
 
# hook for Target Server Information panel called by Browser 
 
proc invokeNetworkInformation {} { 
        global currentBrowserProc 
        global autoResize 
 
        allControlsDestroy Browser 
 
        setupNetworkInfoPage 
 
# hook-in refresh button 
        set currentBrowserProc refreshNetworkInfoPage 
 
# ensure window size reflects Browser's 
        if {$autoResize} { 
                windowSizeCallbackSet Browser "adjustNetworkInfoPage" 
        } 
}
# initial setup of the browser page proc setupNetworkInfoPage {} { global autoResize beginWaitCursor if {$autoResize} { set clientSz [windowClientSizeGet Browser] set maxXExtent [expr [lindex $clientSz 0] - 14] set maxYExtent [expr [lindex $clientSz 1] - 14] } { set maxXExtent 300 set maxYExtent 400 } controlCreate Browser [list hierarchy -name networkInfo \ -x 7 -y 7 -w $maxXExtent -h $maxYExtent] controlStructureSet Browser.networkInfo \ "+ {name flags addr destaddr metric mtu \ {packets {received sent}}\ {errors {input output collision}}}" refreshNetworkInfoPage endWaitCursor }
# adjust page layout when user resizes proc adjustNetworkInfoPage {} { global autoResize if [controlExists Browser.networkInfo] { set clientSz [windowClientSizeGet Browser] if {$autoResize} { set maxXExtent [expr [lindex $clientSz 0] - 14] set maxYExtent [expr [lindex $clientSz 1] - 14] } { set maxXExtent 300 set maxYExtent 160 } controlPositionSet Browser.networkInfo 7 25 controlSizeSet Browser.networkInfo $maxXExtent \ [expr $maxYExtent - 18] } }
# refresh the data displayed by the page proc refreshNetworkInfoPage {} { controlValuesSet Browser.networkInfo [getAllIf] }
# loop through and collect the data for all interfaces proc getAllIf {} { set allIf "" foreach netIf [netIfList] { set thisIf "" set ifInfo [netIfInfo $netIf] lappend thisIf [format "%s%d" [lindex $ifInfo 0] \ [lindex $ifInfo 1]] lappend thisIf [format "(%#x) %s" \ [lindex $ifInfo 2] [ifFlagsFormat \ [lindex $ifInfo 2]]] set ifAddrList [netIfAddrList $netIf] lappend thisIf [ifAddrFormat [lrange $ifAddrList 0 3]] lappend thisIf [ifAddrFormat [lrange $ifAddrList 4 7]] # metric lappend thisIf [lindex $ifInfo 4] # mtu lappend thisIf [expr [lindex $ifInfo 3]] # add packet arrays lappend thisIf [list [expr [lindex $ifInfo 5]] \ [expr [lindex $ifInfo 7]]] # add error/collision counts lappend thisIf [list [expr [lindex $ifInfo 6]] \ [expr [lindex $ifInfo 8]] \ [expr [lindex $ifInfo 9]]] lappend allIf $thisIf } return $allIf }


1:  To have an icon appear in the toolbar button instead of the name, place an X bitmap file with the same name as the button in the directory installDir/resource/bitmaps/Browser/controls. You can do this with any Tornado tool that has a toolbar.