From 2b63a81cec428f6a0fe7b80c9a58e941f9cd4b18 Mon Sep 17 00:00:00 2001 From: Hendrik van Wyk Date: Sun, 8 Feb 2015 16:10:34 +0200 Subject: [PATCH] Added munin logging support. --- cmd/invertergui/main.go | 2 + multiplusmunin.sh | 8 ++ webgui/munin.go | 211 ++++++++++++++++++++++++++++++++++++++++ webgui/webgui.go | 7 ++ webgui/webgui_test.go | 1 + 5 files changed, 229 insertions(+) create mode 100755 multiplusmunin.sh create mode 100644 webgui/munin.go diff --git a/cmd/invertergui/main.go b/cmd/invertergui/main.go index 280acb2..d1b8044 100644 --- a/cmd/invertergui/main.go +++ b/cmd/invertergui/main.go @@ -46,5 +46,7 @@ func main() { source := datasource.NewJSONSource(*url) gui := webgui.NewWebGui(source, 10*time.Second, 100) http.Handle("/", gui) + http.Handle("/munin", http.HandlerFunc(gui.ServeMuninHTTP)) + http.Handle("/muninconfig", http.HandlerFunc(gui.ServeMuninConfigHTTP)) log.Fatal(http.ListenAndServe(":8080", nil)) } diff --git a/multiplusmunin.sh b/multiplusmunin.sh new file mode 100755 index 0000000..66f2d9f --- /dev/null +++ b/multiplusmunin.sh @@ -0,0 +1,8 @@ +#!/bin/bash +case $1 in + config) +curl --fail -s http://localhost:8080/muninconfig + exit $?;; +esac + +curl --fail -s http://localhost:8080/munin diff --git a/webgui/munin.go b/webgui/munin.go new file mode 100644 index 0000000..9866743 --- /dev/null +++ b/webgui/munin.go @@ -0,0 +1,211 @@ +/* +Copyright (c) 2015, Hendrik van Wyk +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +* Neither the name of invertergui nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package webgui + +import ( + "bytes" + "fmt" + "net/http" +) + +type muninData struct { + statusError statusError + timesUpdated int +} + +func (w *WebGui) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) { + muninDat := <-w.muninRespChan + if muninDat.timesUpdated == 0 { + rw.WriteHeader(500) + rw.Write([]byte("No data to return.\n")) + return + } + calcMuninAverages(&muninDat) + + statusErr := &muninDat.statusError + tmpInput := buildTemplateInput(statusErr) + outputBuf := &bytes.Buffer{} + fmt.Fprintf(outputBuf, "multigraph in_batvolt\n") + fmt.Fprintf(outputBuf, "volt.value %s\n", tmpInput.BatVoltage) + fmt.Fprintf(outputBuf, "multigraph in_batcharge\n") + fmt.Fprintf(outputBuf, "charge.value %s\n", tmpInput.BatCharge) + fmt.Fprintf(outputBuf, "multigraph in_batcurrent\n") + fmt.Fprintf(outputBuf, "current.value %s\n", tmpInput.BatCurrent) + fmt.Fprintf(outputBuf, "multigraph in_batpower\n") + fmt.Fprintf(outputBuf, "power.value %s\n", tmpInput.BatPower) + fmt.Fprintf(outputBuf, "multigraph in_mainscurrent\n") + fmt.Fprintf(outputBuf, "currentin.value %s\n", tmpInput.InCurrent) + fmt.Fprintf(outputBuf, "currentout.value %s\n", tmpInput.OutCurrent) + fmt.Fprintf(outputBuf, "multigraph in_mainsvoltage\n") + fmt.Fprintf(outputBuf, "voltagein.value %s\n", tmpInput.InVoltage) + fmt.Fprintf(outputBuf, "voltageout.value %s\n", tmpInput.OutVoltage) + fmt.Fprintf(outputBuf, "multigraph in_mainspower\n") + fmt.Fprintf(outputBuf, "powerin.value %s\n", tmpInput.InPower) + fmt.Fprintf(outputBuf, "powerout.value %s\n", tmpInput.OutPower) + fmt.Fprintf(outputBuf, "multigraph in_mainsfreq\n") + fmt.Fprintf(outputBuf, "freq.value %s\n", tmpInput.InFreq) + + _, err := rw.Write([]byte(outputBuf.String())) + if err != nil { + fmt.Print("%v\n", err) + } +} + +func (w *WebGui) ServeMuninConfigHTTP(rw http.ResponseWriter, r *http.Request) { + output := `multigraph in_batvolt +graph_title Battery Voltage +graph_vlabel Voltage (V) +graph_category inverter +graph_info Battery voltage + +volt.info Voltage of battery +volt.label Voltage of battery (V) + +multigraph in_batcharge +graph_title Battery Charge +graph_vlabel Charge (A h) +graph_category inverter +graph_info Battery charge + +charge.info Estimated charge of battery +charge.label Battery charge (A h) + +multigraph in_batcurrent +graph_title Battery Current +graph_vlabel Current (A) +graph_category inverter +graph_info Battery current + +current.info Battery current +current.label Battery current (A) + +multigraph in_batpower +graph_title Battery Power +graph_vlabel Power (W) +graph_category inverter +graph_info Battery power + +power.info Battery power +power.label Battery power (W) + +multigraph in_mainscurrent +graph_title Mains Current +graph_vlabel Current (A) +graph_category inverter +graph_info Mains current + +currentin.info Input current +currentin.label Input current (A) +currentout.info Output current +currentout.label Output current (A) + +multigraph in_mainsvoltage +graph_title Mains Voltage +graph_vlabel Voltage (V) +graph_category inverter +graph_info Mains voltage + +voltagein.info Input voltage +voltagein.label Input voltage (V) +voltageout.info Output voltage +voltageout.label Output voltage (V) + +multigraph in_mainspower +graph_title Mains Power +graph_vlabel Power (VA) +graph_category inverter +graph_info Mains power + +powerin.info Input power +powerin.label Input power (VA) +powerout.info Output power +powerout.label Output power (VA) + +multigraph in_mainsfreq +graph_title Mains frequency +graph_vlabel Frequency (Hz) +graph_category inverter +graph_info Mains frequency + +freq.info Input frequency +freq.label Input frequency (Hz) +` + + _, err := rw.Write([]byte(output)) + if err != nil { + fmt.Print("%v\n", err) + } +} + +//Munin only samples once every 5 minutes so averages have to be calculated for some values. +func calcMuninValues(muninDat *muninData, newStatus *statusError) { + muninDat.timesUpdated += 1 + muninVal := &muninDat.statusError + muninVal.status.OutCurrent += newStatus.status.OutCurrent + muninVal.status.InCurrent += newStatus.status.InCurrent + muninVal.status.BatCurrent += newStatus.status.BatCurrent + + muninVal.status.OutVoltage += newStatus.status.OutVoltage + muninVal.status.InVoltage += newStatus.status.InVoltage + muninVal.status.BatVoltage += newStatus.status.BatVoltage + + muninVal.status.InFreq = newStatus.status.InFreq + + muninVal.chargeLevel = newStatus.chargeLevel + muninVal.status.Leds = newStatus.status.Leds +} + +func calcMuninAverages(muninDat *muninData) { + muninVal := &muninDat.statusError + muninVal.status.OutCurrent /= float64(muninDat.timesUpdated) + muninVal.status.InCurrent /= float64(muninDat.timesUpdated) + muninVal.status.BatCurrent /= float64(muninDat.timesUpdated) + + muninVal.status.OutVoltage /= float64(muninDat.timesUpdated) + muninVal.status.InVoltage /= float64(muninDat.timesUpdated) + muninVal.status.BatVoltage /= float64(muninDat.timesUpdated) +} + +func zeroMuninValues(muninDat *muninData) { + muninDat.timesUpdated = 0 + muninVal := &muninDat.statusError + muninVal.status.OutCurrent = 0 + muninVal.status.InCurrent = 0 + muninVal.status.BatCurrent = 0 + + muninVal.status.OutVoltage = 0 + muninVal.status.InVoltage = 0 + muninVal.status.BatVoltage = 0 + + muninVal.status.InFreq = 0 + + muninVal.chargeLevel = 0 +} diff --git a/webgui/webgui.go b/webgui/webgui.go index f93a59c..cabc60a 100644 --- a/webgui/webgui.go +++ b/webgui/webgui.go @@ -66,6 +66,8 @@ type WebGui struct { respChan chan statusError stopChan chan struct{} template *template.Template + + muninRespChan chan muninData } func NewWebGui(source datasource.DataSource, pollRate time.Duration, batteryCapacity float64) *WebGui { @@ -73,6 +75,7 @@ func NewWebGui(source datasource.DataSource, pollRate time.Duration, batteryCapa wg.source = source wg.reqChan = make(chan *statusError) wg.respChan = make(chan statusError) + wg.muninRespChan = make(chan muninData) wg.stopChan = make(chan struct{}) var err error wg.template, err = template.New("thegui").Parse(htmlTemplate) @@ -164,6 +167,7 @@ func (w *WebGui) dataPoll(pollRate time.Duration, batteryCapacity float64) { ticker := time.NewTicker(pollRate) tracker := NewChargeTracker(batteryCapacity) var statusErr statusError + var muninValues muninData go w.getStatus() gettingStatus := true for { @@ -184,9 +188,12 @@ func (w *WebGui) dataPoll(pollRate time.Duration, batteryCapacity float64) { tracker.Reset() } statusErr.chargeLevel = tracker.CurrentLevel() + calcMuninValues(&muninValues, &statusErr) } gettingStatus = false case w.respChan <- statusErr: + case w.muninRespChan <- muninValues: + zeroMuninValues(&muninValues) case <-w.stopChan: return } diff --git a/webgui/webgui_test.go b/webgui/webgui_test.go index d6f62a3..40c6f6a 100644 --- a/webgui/webgui_test.go +++ b/webgui/webgui_test.go @@ -94,6 +94,7 @@ var templateInputTests = []templateTest{ BatCurrent: "-10.000", BatPower: "-250.000", InFreq: "50.000", + BatCharge: "0.000", Leds: []string{"Mains", "Float"}}, }, }