Complete µWeb Chart Example

chart.page

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
title "Charts"

access {* rw}
storage page

summary {Display system history data}

field type {
    stdmode hidden
    default 0
}

field period {
    stdmode hidden
    default 0
}

init {
    set pages {
        {"Temperatures" "System Temperatures" chart_temp}
        {"Voltages" "System Voltages" chart_volts}
    }

    set periods {Hour Day Week Month}
}

display {
    # Dynamically display tabs
    set type [cgi get type]
    set period [cgi get period]
    set pername [lindex $periods $period]

    set n 0
    foreach line $pages {
        lassign $line label title subpage
        lappend tab1 [cgi href $page type $n period $period] $label
        lappend titles $title
        lappend subpages $subpage
        incr n
    }

    set n 0
    foreach pname $periods {
        lappend tab2 [cgi href $page type $type period $n] $pname
        incr n
    }

    cgi tabs $type $tab1
    cgi tabs $period $tab2

    # Display bar
    cgi display row {
        html str div class=bar "[lindex $titles $type] - $pername"
    }

    # Display chart image
    cgi display row {
        html tag img src=[cgi href [lindex $subpages $type] \
        period [string tolower $pername]]
    }
}

# This is a macro to enable auto-refresh on this page
auto_refresh_page type period

# vim: se ts=4 sw=4 noet:

chart_temp.page

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# This page is dynamically invoked by chart.page.
storage none
pageformat raw

field period {
    stdmode hidden
    default hour
}

display {
    package require rrdgraph

    set r [rrdgraph $page [cgi get period]]
    $r stdinit
    $r title "System Temperatures"
    $r ylabel "\u00b0Celsius"
    $r line l2 buctemp -label "BUC" -format "%3.1lf" -units "\u00b0Celsius" -col #6666EE
    $r line l3 airtemp -label "Air" -format "%3.1lf" -units "\u00b0Celsius" -col #11AA11
    $r line l1 hwtemp -label "Hardware" -format "%3.1lf" -units "\u00b0Celsius" -col #1111AA

    $r output
    $r close
}

chart_volts.page

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# This page is dynamically invoked by chart.page.
storage none
pageformat raw

field period {
    stdmode hidden
    default hour
}

display {
    package require rrdgraph

    set r [rrdgraph $page [cgi get period]]
    $r stdinit
    $r title "Voltages"
    $r ylabel "Volts"
    $r line l1 voltage -label "H/W" -format "%3.1lf" -units "Volts" -col #1111AA
    $r output
    $r close
}

rrdgraph.tcl

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# Common functions for rrdtool graphing pages

proc rrdgraph {graphname period} {

    switch -glob -- $period \
        d* {
            set duration day
            set info(start) e-1d
            set info(step) 300
            set info(type) AVERAGE
            set info(title) "24 Hours"
        } \
        w* {
            set duration week
            set info(start) e-1w
            set info(step) 1800
            set info(type) AVERAGE
            set info(title) "1 Week"
        } \
        m* {
            set duration month
            set info(start) e-1month
            set info(step) 3600
            set info(type) AVERAGE
            set info(title) "1 Month"
        } \
        y* {
            set duration year
            set info(start) e-1year
            set info(step) 43200
            set info(type) AVERAGE
            set info(title) "1 year"
        } \
        * {
            set duration hour
            set info(start) e-1h
            set info(step) 30
            set info(type) LAST
            set info(title) "1 Hour"
        }
        set host [env HOST ""]
        set info(rrd) $host/settings/logging/system.rrd
        file mkdir $host/tmp/rrd/
        set info(png) $host/tmp/rrd/${graphname}_${duration}.png
        set params {}

        lappend params -s $info(start) -e now -S $info(step)

    lambda {cmd args} {info params} {
        set params [rrdgraph.$cmd $params $info {*}$args]
    }
}

proc rrdgraph.close {params info} {
    lassign [info level 0] r
    rename $r ""
}

proc rrdgraph.stdinit {params info} {
    rrdgraph.size $params $info 620 200
}

proc rrdgraph.size {params info w h} {
    lappend params -w $w -h $h
}

proc rrdgraph.title {params info title} {
    lappend params -t "$title ($info(title))"
}

proc rrdgraph.ylabel {params info ylabel} {
    lappend params -v $ylabel
}

proc rrdgraph.comment {params info text} {
    lappend params COMMENT:[rrdgraph._escape $text]
}

proc rrdgraph.params {params info args} {
    lappend params {*}$args
}

# Escapes a label to convert \n to \\n and : to \\:
proc rrdgraph._escape {label} {
    string map [list \n \\n : \\:] $label
}

# Optional arguments are:
#    -col     graph colour, otherwise uses a default
#    -width   width of the line, otherwise 2
#    -label   label for the graph, otherwise none
#    -format  format string for the x axis, otherwise none
#    -units   units string, otherwise none
#    -multiplier multiply the raw value by this, otherwise 1
proc rrdgraph.line {params info name var args} {
    set opts [dict merge {-col #1111AA -width 2 -label "" -format "" -units "" -multiplier 1} $args]
    set opts(-label) [rrdgraph._escape $opts(-label)]

    lappend params DEF:$name=$info(rrd):$var:$info(type)
    lappend params CDEF:g_$name=$name,$opts(-multiplier),*
    lappend params LINE$opts(-width):g_$name$opts(-col):$opts(-label)
    if {$opts(-format) ne ""} {
        set opts(-format) [rrdgraph._escape $opts(-format)]
        set opts(-units) [rrdgraph._escape $opts(-units)]
        lappend params VDEF:min$name=g_$name,MINIMUM "GPRINT:min$name:Min $opts(-format)"
        lappend params VDEF:max$name=g_$name,MAXIMUM "GPRINT:max$name:Max $opts(-format)"
        lappend params VDEF:avg$name=g_$name,AVERAGE "GPRINT:avg$name:Avg $opts(-format) $opts(-units)\\n"
    }
    return $params
}

# Optional arguments are:
#    -col    graph colour, otherwise uses a default
#    -label  label for the graph, otherwise none
proc rrdgraph.area {params info name var args} {
    set opts [dict merge {-col #66ee66 -label ""} $args]
    set opts(-label) [rrdgraph._escape $opts(-label)]

    lappend params DEF:$name=$info(rrd):$var:$info(type)
    lappend params AREA:$name$opts(-col):$opts(-label)
}

# Optional arguments are:
#    -col    graph colour, otherwise uses a default
#    -value  height of the line, otherwise 1
#    -width  width of the line, otherwise 2
#    -label  label for the graph, otherwise none
proc rrdgraph.bool {params info name var args} {
    set opts [dict merge {-col #888888 -label "" -value 1 -width 2} $args]
    set opts(-label) [rrdgraph._escape $opts(-label)]

    lappend params DEF:$name=$info(rrd):$var:$info(type)
    lappend params CDEF:g_$name=$name,$opts(-value),*
    lappend params LINE$opts(-width):g_$name$opts(-col):$opts(-label)
}

proc rrdgraph.output {params info} {
    # If it was created less than 'step' seconds ago, don't recreate
    set now [clock seconds]
    if {![file exists $info(png)] || [file mtime $info(png)] < $now - $info(step)} {
        # Note we use locking here to avoid trying to create multiple graphs at once
        exec setlock [env HOST ""]/var/run/rrdgraph.lock rrdtool graph $info(png) {*}$params
    }

    cgi nodisplay

    cgi http header Content-Type image/png
    cgi http header Content-Length [file size $info(png)]
    cgi http response 200

    set f [open $info(png)]
    $f copyto stdout
    $f close
}

proc rrdgraph.debug {params info} {
    puts [list rrdtool graph $info(png) {*}$params]
}

# vim: se ts=4: