# copyright (C) 1997-2006 Jean-Luc Fontaine (mailto:jfontain@free.fr)
# this program is free software: please read the COPYRIGHT file enclosed in this package or use the Help Copyright menu

# $Id: random.tcl,v 1.67 2006/04/02 19:28:52 jfontain Exp $

# module sample file with random data

# corresponding pkgIndex.tcl file content:
# package ifneeded random 1.45 [list source [file join $dir random.tcl]]

# Note: see version number management requirements in moodss HTML documentation (Module Development section)
package provide random [lindex {$Revision: 1.67 $} 1]

namespace eval random {                                                                    ;# namespace name must match package name
    # Module data is arranged in a single array that can be used by many views, such as tables, graphs, pies,...
    # It contains configuration values as well as dynamic data (see below and update procedure).

    # Array members are:
    # - updates: a counter traced on write by viewers monitoring this module data. Must be incremented as soon as the data has been
    #   updated. Must be initialized in the namespace body in all cases as it is used for automatic module detection (may not be
    #   changed in the initialize procedure).
    # Column numbers must be positive, starting from 0 included. Column members are:
    #   - label: the title displayed on the top cell of the data column.
    #   - type: valid types are those that the lsort command can handle, plus the clock type
    #   - message: help text that appears in a widget tip when the mouse cursor is left over the column title cell for a short
    #     while.
    #   - anchor: optional member. Column data is either centered by default (center), tucked to the left (left) or right side
    #     (right) of the column.
    # - pollTimes: in seconds. A list with the default poll time in first position. The other times can be in any order, as the core
    #   will sort them and use the lowest value as the minimum poll time value.
    #   The minimum poll seconds must be a reasonable value depending on the processing time of the update procedure.
    # - indices: an optional list that specifies the data columns that should be displayed. If not specified, all the data columns
    #   are visible. Note: visibleColumns, the previous keyword for that function, is OBSOLETE (but still supported) after moodss
    #   versions 8.22 and 11.7.
    # - columns: total number of data columns to be displayed (OBSOLETE, no longer required).
    # - sort: an optional list that defines the initial column to be used for sorting the table data and in which direction. The
    #   specified column must be visible (see indices member above).
    # - indexColumns: a list that specifies the columns required to uniquely identify a row in the table. It is optional and
    #   defaults to the column 0, unless of course you do not have a 0 column index, in which case it is mandatory to specify this
    #   list. When specified, all the columns in the list must be visible (see indices member above and in views below).
    # - helpText: displayed when the module help is launched from the main window help menu. Can be plain text or HTML formatted
    #   (<HTML> and <BODY> tags required), in which case it is properly rendered in the module help window (note: tables, frames,
    #   and may other tags are not supported: stick to formatted text at the moment).
    # - views: optional. If specified, it defines one or more views to be used in place of the default view. One table will be
    #   displayed per view. Each view is a list of array members, suitable as an argument to an "array set" command. For each view,
    #   1 member must be defined: indices (syntax and usage identical to the corresponding table member).
    #   2 members are optional: sort (syntax and usage identical to the corresponding table member), and swap, a boolean, specifies
    #   whether the data is displayed in a table with columns and rows swapped (useful when data has only a single or a couple of
    #   rows permanently).
    # - switches: optional. A list of switch / boolean pairs. A switch is a single letter or a string prepended with the - or +
    #   sign. The boolean value (0 or 1) specifies whether the switch takes an argument. If the switches member exists, an
    #   appropriate initialize procedure must be provided by the module (see initialize procedure example in this module).
    #   The core will take care of parsing the command line and reject any invalid switch / value combination for the module.
    #   The switches value may not be changed in the initialize procedure.
    #   Note: if a --daemon special option exists (must take no arguments), it is automatically set when in daemon mode (moomps).
    # - identifier: optional, ignore for moodss versions before 8.0. A string that uniquely identifies this module. It will be
    #   displayed by the core in the initial data tables title area and data cell labels in viewers. This feature can be used for
    #   example in modules that gather data from a remote host: in such a case, the identifier could be set to dataType(hostName).
    # - resizableColumns: optional. A boolean value (0 or 1) which specifies whether displayed data table(s) columns can be manually
    #   resized by the user with the mouse. Not available on a per view basis.
    # - persistent: optional (considered false if missing). A boolean value that tells whether row numbers (used by the core, along
    #   with the column numbers, to identify data cells) are identically mapped from data row keys across module instances.
    # - 64Bits: optional. A boolean value (0 or 1) which specifies whether row numbers are unsigned 64 bit integers.
    #   If absent or false, row numbers as unsigned 32 bit integers are assumed.
    array set data {
        updates 0
        0,label name 0,type ascii 0,message {user name}
        1,label cpu 1,type real 1,message {cpu usage in percent}
        2,label disk 2,type integer 2,message {disk usage in megabytes}
        3,label memory 3,type integer 3,message {memory usage in kilobytes}
        4,label command 4,type dictionary 4,message {command name} 4,anchor left
        pollTimes {10 5 20 30 60 120 300}
        indexColumns {0 4}
        views {
            {indices {0 1 3 4} sort {1 decreasing}}
            {indices {0 2 4} sort {2 decreasing}}
        }
        persistent 1
        64Bits 1
        switches {-a 0 --asynchronous 0 -i 0 --identify 0 --daemon 0}
    }
    if {\
       ([info exists ::env(LC_ALL)] && [string match ja* $::env(LC_ALL)]) ||\
       ([info exists ::env(LANG)] && [string match ja* $::env(LANG)])\
    } {
        set file [open random-ja.htm]
    } else {
        set file [open random.htm]                                           ;# module directory is current during the loading phase
    }
    set data(helpText) [read $file]                                   ;# initialize HTML help data from file in module sub-directory
    close $file

    # The initialize procedure, if it exists, is invoked by the core before any update occurs (update procedure invocation if the
    # module is synchronous). It can be used for module setup, although it is rather redundant with inline module namespace code
    # (namespace code outside of any module namespace procedure) for a module with no command line arguments support.
    # The initialize procedure is optional when the module does not support command line arguments, and in such a case takes no
    # arguments.
    # The initialize procedure is mandatory when the module supports command line arguments, and in such a case takes an array name
    # as sole argument. The array contains the switched options values, indexed by switch. For example, if the command line was:
    # $ moodss random --asynchronous --other-option value -x 1234
    # the options array will contain:
    #   options(--asynchronous) =
    #   options(--other-option) = value
    #   options(-x)             = 1234
    # Note that the --asynchronous member value is empty as that switch takes no argument.
    # For the above example, switches would have been defined as:
    #   switches {-a 0 --asynchronous 0 --other-option 1 -x 1}
    # In all cases, data members other than updates and switches can be set or updated in the initialize procedure, and taken into
    # account by the core.
    proc initialize {optionsName} {
        upvar 1 $optionsName options
        variable asynchronous
        variable data

        set asynchronous [expr {[info exists options(-a)] || [info exists options(--asynchronous)]}]
        if {$asynchronous} {
            # for a module to be asynchronous, the pollTimes member must be a single negative integer value. It then represents the
            # preferred time interval for viewers that require one.
            array set data {pollTimes -10}
            after 3000 random::update                                                                             ;# boot simulation
        }
        if {[info exists options(-i)] || [info exists options(--identify)]} {                 ;# generate a unique module identifier
            set data(identifier) "random [expr {int(rand() * 100)}]"
        }
    }

    # The terminate procedure if it exists is invoked by the core when the module is unloaded dynamically.
    proc terminate {} {
        # We could do some cleanup chores right here, but that cannot potentially hang, for it would also hang the application.
    }

    # The dynamic data array index is the row number followed by the column number separated by a comma.
    # The column number must start from 0 up to the total number of columns minus 1 (no holes are allowed in the column sequence)
    # The row number can take any positive integer value (between 0 and 18446744073709551615) and be defined in any order. Holes in
    # the rows sequence are allowed as well: row numbers need not be consecutive (also see HTML developer documentation on handling
    # unsigned 32 and 64 bit integers, database implications and row/key relationship)
    # If a row has to disappear, all data cells for that row must disappear, that is cells for all columns.
    # Voidness for numeric data cells (integer or real type) takes the form of the ? character. Reminder: as long as a data row
    # exists, all its data cells must exist. Thus voidness cannot be expressed by non-existence.

    # The update procedure is mandatory only for synchronous modules, as it is never invoked by the core for asynchronous modules.
    # Its function is to update module data.
    proc update {} {
        variable data
        variable asynchronous

        array set data "
            0,0 John\\nWo 0,1 [format %.1f [expr {rand() * 30}]] 0,2 [expr {100 + int(rand() * 50)}]
                0,3 [expr {10 + int(rand() * 50)}] 0,4 cc
            2147483647,0 Bill 2147483647,1 [format %.1f [expr {rand() * 3}]] 2147483647,2 [expr {300 + int(rand() * 100)}]
                2147483647,3 [expr {30 + int(rand() * 100)}] 2147483647,4 xedit
            2147483648,0 Anny\\nDoe 2147483648,1 [format %.1f [expr {rand() * 5}]] 2147483648,2 [expr {200 + int(rand() * 30)}]
                2147483648,3 [expr {20 + int(rand() * 30)}] 2147483648,4 ps
            4294967295,0 Peter\\nWard 4294967295,1 [format %.1f [expr {rand() * 8}]] 4294967295,2 [expr {50 + int(rand() * 10)}]
                4294967295,3 [expr {5 + int(rand() * 10)}] 4294967295,4 ls
            9223372036854775807,0 Laura 9223372036854775807,1 [format %.1f [expr {rand() * 20}]]
                9223372036854775807,2 [expr {90 + int(rand() * 20)}] 9223372036854775807,3 [expr {9 + int(rand() * 20)}]
                9223372036854775807,4 emacs
            9223372036854775808,0 Robert 9223372036854775808,1 [format %.1f [expr {rand() * 10}]]
                9223372036854775808,2 [expr {500 + int(rand() * 150)}] 9223372036854775808,3 [expr {50 + int(rand() * 150)}]
                9223372036854775808,4 top
            18446744073709551615,0 Laura 18446744073709551615,1 [format %.1f [expr {rand() * 30}]]
                18446744073709551615,2 [expr {100 + int(rand() * 50)}] 18446744073709551615,3 [expr {10 + int(rand() * 50)}]
                18446744073709551615,4 cc
        "
        incr data(updates)                                   ;# increment updates counter so that update can be detected by the core
        if {$asynchronous} {                                                         ;# simulate asynchronous operation if requested
            after [expr {2000 + round(rand() * 9000)}] random::update
        }
    }
}
