From a52af88bffb9af03534d7b0b35123d1f255bd674 Mon Sep 17 00:00:00 2001 From: Hendrik van Wyk Date: Sat, 16 Sep 2017 15:37:58 +0200 Subject: [PATCH] Use mk2if instead of datasource as data source. This removes support for the python based mk2daemon. --- cmd/invertergui/main.go | 45 ++++++++++++++--- datasource/datapoller.go | 99 ------------------------------------ datasource/decode_test.go | 69 -------------------------- datasource/jsondecode.go | 77 ---------------------------- datasource/poller_test.go | 91 ---------------------------------- webgui/htmltemplate.go | 9 +++- webgui/munin.go | 76 ++++++++++++++-------------- webgui/prometheus.go | 12 ++--- webgui/webgui.go | 102 ++++++++++++-------------------------- webgui/webgui_test.go | 56 ++++++++------------- 10 files changed, 144 insertions(+), 492 deletions(-) delete mode 100644 datasource/datapoller.go delete mode 100644 datasource/decode_test.go delete mode 100644 datasource/jsondecode.go delete mode 100644 datasource/poller_test.go diff --git a/cmd/invertergui/main.go b/cmd/invertergui/main.go index c26f44f..519bddd 100644 --- a/cmd/invertergui/main.go +++ b/cmd/invertergui/main.go @@ -32,23 +32,54 @@ package main import ( "flag" - "github.com/hpdvanwyk/invertergui/datasource" + "github.com/hpdvanwyk/invertergui/mk2if" "github.com/hpdvanwyk/invertergui/webgui" + "github.com/mikepb/go-serial" "github.com/prometheus/client_golang/prometheus/promhttp" + "io" "log" + "net" "net/http" - "time" ) func main() { - url := flag.String("url", "http://localhost:9005", "The url of the multiplus JSON interface.") - capacity := flag.Float64("capacity", 100, "The capacity of the batteries in the system.") addr := flag.String("addr", ":8080", "TCP address to listen on.") + + tcp := flag.Bool("tcp", false, "Use TCP instead of TTY") + ip := flag.String("ip", "localhost:8139", "IP to connect when using tcp connection.") + dev := flag.String("dev", "/dev/ttyUSB0", "TTY device to use.") flag.Parse() - source := datasource.NewJSONSource(*url) - poller := datasource.NewDataPoller(source, 10*time.Second) - gui := webgui.NewWebGui(poller, *capacity) + var p io.ReadWriteCloser + var err error + var tcpAddr *net.TCPAddr + + if *tcp { + tcpAddr, err = net.ResolveTCPAddr("tcp", *ip) + if err != nil { + panic(err) + } + p, err = net.DialTCP("tcp", nil, tcpAddr) + if err != nil { + panic(err) + } + } else { + options := serial.RawOptions + options.BitRate = 2400 + options.Mode = serial.MODE_READ_WRITE + p, err = options.Open(*dev) + if err != nil { + panic(err) + } + } + defer p.Close() + mk2, err := mk2if.NewMk2Connection(p) + defer mk2.Close() + if err != nil { + panic(err) + } + + gui := webgui.NewWebGui(mk2) http.Handle("/", gui) http.Handle("/munin", http.HandlerFunc(gui.ServeMuninHTTP)) http.Handle("/muninconfig", http.HandlerFunc(gui.ServeMuninConfigHTTP)) diff --git a/datasource/datapoller.go b/datasource/datapoller.go deleted file mode 100644 index c1cdf30..0000000 --- a/datasource/datapoller.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -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 datasource - -import ( - "sync" - "time" -) - -type DataPoller interface { - C() chan *Status - Stop() -} - -type Status struct { - MpStatus MultiplusStatus - Time time.Time - Err error -} - -type poller struct { - source DataSource - rate time.Duration - statusChan chan *Status - stop chan struct{} - wg sync.WaitGroup -} - -func NewDataPoller(source DataSource, pollRate time.Duration) DataPoller { - this := &poller{ - source: source, - rate: pollRate, - statusChan: make(chan *Status), - stop: make(chan struct{}), - } - this.wg.Add(1) - go this.poll() - return this -} - -func (this *poller) C() chan *Status { - return this.statusChan -} - -func (this *poller) Stop() { - close(this.stop) - this.wg.Wait() -} - -func (this *poller) poll() { - ticker := time.NewTicker(this.rate) - this.doPoll() - for { - select { - case <-ticker.C: - this.doPoll() - case <-this.stop: - ticker.Stop() - close(this.statusChan) - this.wg.Done() - return - } - } -} - -func (this *poller) doPoll() { - tmp := new(Status) - tmp.Err = this.source.GetData(&tmp.MpStatus) - tmp.Time = time.Now() - this.statusChan <- tmp -} diff --git a/datasource/decode_test.go b/datasource/decode_test.go deleted file mode 100644 index 1471e16..0000000 --- a/datasource/decode_test.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -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 datasource - -import ( - "net/http" - "net/http/httptest" - "reflect" - "testing" -) - -var sampleJSON string = `{"outCurrent": 1.19, -"leds": [0, 0, 0, 0, 1, 0, 0, 1], -"batVoltage": 26.63, -"inCurrent": 1.39, -"outVoltage": 235.3, -"inVoltage": 235.3, -"inFreq": 51.3, -"batCurrent": 0.0, -"outFreq": 735.3}` - -func returnJson(resp http.ResponseWriter, req *http.Request) { - resp.Write([]byte(sampleJSON)) -} - -func TestFetchStatus(t *testing.T) { - //setup test server - testServer := httptest.NewServer(http.HandlerFunc(returnJson)) - - var status MultiplusStatus - source := NewJSONSource(testServer.URL) - err := source.GetData(&status) - if err != nil { - t.Errorf("Unmarshal gave: %v", err) - } - expected := MultiplusStatus{1.19, 1.39, 235.3, 235.3, 26.63, 0, 51.3, 735.3, []int{0, 0, 0, 0, 1, 0, 0, 1}} - if !reflect.DeepEqual(status, expected) { - t.Errorf("JSON string did not decode as expected.") - } - testServer.Close() -} diff --git a/datasource/jsondecode.go b/datasource/jsondecode.go deleted file mode 100644 index 0505a7c..0000000 --- a/datasource/jsondecode.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -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 datasource - -import ( - "encoding/json" - "net/http" -) - -type DataSource interface { - GetData(*MultiplusStatus) error -} - -type MultiplusStatus struct { - OutCurrent float64 `json:"outCurrent"` - InCurrent float64 `json:"inCurrent"` - OutVoltage float64 `json:"outVoltage"` - InVoltage float64 `json:"inVoltage"` - BatVoltage float64 `json:"batVoltage"` - BatCurrent float64 `json:"batCurrent"` - InFreq float64 `json:"inFreq"` - OutFreq float64 `json:"outFreq"` - Leds []int `json:"leds"` -} - -type source struct { - url string -} - -func NewJSONSource(url string) DataSource { - return &source{url: url} -} - -func (s *source) GetData(status *MultiplusStatus) error { - resp, err := http.Get(s.url) - if err != nil { - return err - } - dec := json.NewDecoder(resp.Body) - err = dec.Decode(status) - if err != nil { - return err - } - err = resp.Body.Close() - if err != nil { - return err - } - return nil -} diff --git a/datasource/poller_test.go b/datasource/poller_test.go deleted file mode 100644 index 1cd8897..0000000 --- a/datasource/poller_test.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -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 datasource - -import ( - "errors" - "testing" - "time" -) - -type mockDataSource struct { - currentMock int - shouldBreak bool -} - -func (this *mockDataSource) GetData(data *MultiplusStatus) error { - if this.shouldBreak { - return errors.New("Do not be alarmed. This is only a test.") - } - data.BatCurrent = float64(this.currentMock) - this.currentMock++ - return nil -} - -func TestOnePoll(t *testing.T) { - poller := NewDataPoller(&mockDataSource{currentMock: 100}, 1*time.Millisecond) - statChan := poller.C() - status := <-statChan - if status.MpStatus.BatCurrent != 100 { - t.Errorf("Incorrect data passed from data source.") - } - if status.Time.IsZero() { - t.Errorf("Time not set.") - } - poller.Stop() -} - -func TestMultiplePolls(t *testing.T) { - poller := NewDataPoller(&mockDataSource{currentMock: 100}, 1*time.Millisecond) - statChan := poller.C() - expect := 100 - for i := 0; i < 100; i++ { - status := <-statChan - if status.MpStatus.BatCurrent != float64(expect) { - t.Errorf("Incorrect data passed from data source.") - } - expect++ - if status.Time.IsZero() { - t.Errorf("Time not set.") - } - } - poller.Stop() -} - -func TestError(t *testing.T) { - poller := NewDataPoller(&mockDataSource{shouldBreak: true}, 1*time.Millisecond) - statChan := poller.C() - status := <-statChan - if status.Err == nil { - t.Errorf("Error not correctly propagated.") - } - poller.Stop() -} diff --git a/webgui/htmltemplate.go b/webgui/htmltemplate.go index 56be4db..e11fcbb 100644 --- a/webgui/htmltemplate.go +++ b/webgui/htmltemplate.go @@ -40,7 +40,11 @@ var htmlTemplate string = ` - {{if .Error}}

Error encountered: {{.Error}}

{{end}} + {{if .Error}}

Errors encountered:

+ {{range .Error}} +
{{.}}
+ {{end}} + {{end}}
Date: {{.Date}}
@@ -55,6 +59,7 @@ var htmlTemplate string = `
Output Current: {{.OutCurrent}} A
Output Voltage: {{.OutVoltage}} V
+
Output Frequency: {{.OutFreq}} Hz
Output Power: {{.OutPower}} VA
@@ -68,7 +73,7 @@ var htmlTemplate string = `
Battery Current: {{.BatCurrent}} A
Battery Voltage: {{.BatVoltage}} V
Battery Power: {{.BatPower}} W
-
Battery Charge: {{.BatCharge}} A h
+
Battery Charge: {{.BatCharge}} %
` diff --git a/webgui/munin.go b/webgui/munin.go index 3db13f1..161f09e 100644 --- a/webgui/munin.go +++ b/webgui/munin.go @@ -33,12 +33,12 @@ package webgui import ( "bytes" "fmt" + "github.com/hpdvanwyk/invertergui/mk2if" "net/http" - "time" ) type muninData struct { - statusP statusProcessed + status mk2if.Mk2Info timesUpdated int } @@ -51,8 +51,8 @@ func (w *WebGui) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) { } calcMuninAverages(&muninDat) - statusP := &muninDat.statusP - tmpInput := buildTemplateInput(statusP, time.Now()) + status := muninDat.status + tmpInput := buildTemplateInput(&status) outputBuf := &bytes.Buffer{} fmt.Fprintf(outputBuf, "multigraph in_batvolt\n") fmt.Fprintf(outputBuf, "volt.value %s\n", tmpInput.BatVoltage) @@ -72,7 +72,8 @@ func (w *WebGui) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) { 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) + fmt.Fprintf(outputBuf, "freqin.value %s\n", tmpInput.InFreq) + fmt.Fprintf(outputBuf, "freqout.value %s\n", tmpInput.OutFreq) _, err := rw.Write([]byte(outputBuf.String())) if err != nil { @@ -92,12 +93,12 @@ volt.label Voltage of battery (V) multigraph in_batcharge graph_title Battery Charge -graph_vlabel Charge (A h) +graph_vlabel Charge (%) graph_category inverter graph_info Battery charge charge.info Estimated charge of battery -charge.label Battery charge (A h) +charge.label Battery charge (%) multigraph in_batcurrent graph_title Battery Current @@ -156,8 +157,10 @@ graph_vlabel Frequency (Hz) graph_category inverter graph_info Mains frequency -freq.info Input frequency -freq.label Input frequency (Hz) +freqin.info In frequency +freqin.label In frequency (Hz) +freqout.info Out frequency +freqout.label Out frequency (Hz) ` _, err := rw.Write([]byte(output)) @@ -167,46 +170,47 @@ freq.label Input frequency (Hz) } //Munin only samples once every 5 minutes so averages have to be calculated for some values. -func calcMuninValues(muninDat *muninData, newStatus *statusProcessed) { +func calcMuninValues(muninDat *muninData, newStatus *mk2if.Mk2Info) { muninDat.timesUpdated += 1 - muninVal := &muninDat.statusP - muninVal.status.OutCurrent += newStatus.status.OutCurrent - muninVal.status.InCurrent += newStatus.status.InCurrent - muninVal.status.BatCurrent += newStatus.status.BatCurrent + muninVal := &muninDat.status + muninVal.OutCurrent += newStatus.OutCurrent + muninVal.InCurrent += newStatus.InCurrent + muninVal.BatCurrent += newStatus.BatCurrent - muninVal.status.OutVoltage += newStatus.status.OutVoltage - muninVal.status.InVoltage += newStatus.status.InVoltage - muninVal.status.BatVoltage += newStatus.status.BatVoltage + muninVal.OutVoltage += newStatus.OutVoltage + muninVal.InVoltage += newStatus.InVoltage + muninVal.BatVoltage += newStatus.BatVoltage - muninVal.status.InFreq = newStatus.status.InFreq + muninVal.InFrequency = newStatus.InFrequency + muninVal.OutFrequency = newStatus.OutFrequency - muninVal.chargeLevel = newStatus.chargeLevel - muninVal.status.Leds = newStatus.status.Leds + muninVal.ChargeState = newStatus.ChargeState } func calcMuninAverages(muninDat *muninData) { - muninVal := &muninDat.statusP - muninVal.status.OutCurrent /= float64(muninDat.timesUpdated) - muninVal.status.InCurrent /= float64(muninDat.timesUpdated) - muninVal.status.BatCurrent /= float64(muninDat.timesUpdated) + muninVal := &muninDat.status + muninVal.OutCurrent /= float64(muninDat.timesUpdated) + muninVal.InCurrent /= float64(muninDat.timesUpdated) + muninVal.BatCurrent /= float64(muninDat.timesUpdated) - muninVal.status.OutVoltage /= float64(muninDat.timesUpdated) - muninVal.status.InVoltage /= float64(muninDat.timesUpdated) - muninVal.status.BatVoltage /= float64(muninDat.timesUpdated) + muninVal.OutVoltage /= float64(muninDat.timesUpdated) + muninVal.InVoltage /= float64(muninDat.timesUpdated) + muninVal.BatVoltage /= float64(muninDat.timesUpdated) } func zeroMuninValues(muninDat *muninData) { muninDat.timesUpdated = 0 - muninVal := &muninDat.statusP - muninVal.status.OutCurrent = 0 - muninVal.status.InCurrent = 0 - muninVal.status.BatCurrent = 0 + muninVal := &muninDat.status + muninVal.OutCurrent = 0 + muninVal.InCurrent = 0 + muninVal.BatCurrent = 0 - muninVal.status.OutVoltage = 0 - muninVal.status.InVoltage = 0 - muninVal.status.BatVoltage = 0 + muninVal.OutVoltage = 0 + muninVal.InVoltage = 0 + muninVal.BatVoltage = 0 - muninVal.status.InFreq = 0 + muninVal.InFrequency = 0 + muninVal.OutFrequency = 0 - muninVal.chargeLevel = 0 + muninVal.ChargeState = 0 } diff --git a/webgui/prometheus.go b/webgui/prometheus.go index 35b0264..ab624b1 100644 --- a/webgui/prometheus.go +++ b/webgui/prometheus.go @@ -31,6 +31,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package webgui import ( + "github.com/hpdvanwyk/invertergui/mk2if" "github.com/prometheus/client_golang/prometheus" ) @@ -116,10 +117,10 @@ func newPrometheusUpdater() *prometheusUpdater { return tmp } -func (pu *prometheusUpdater) updatePrometheus(newStatus *statusProcessed) { - s := newStatus.status +func (pu *prometheusUpdater) updatePrometheus(newStatus *mk2if.Mk2Info) { + s := newStatus pu.batteryVoltage.Set(s.BatVoltage) - pu.batteryCharge.Set(newStatus.chargeLevel) + pu.batteryCharge.Set(newStatus.ChargeState * 100) pu.batteryCurrent.Set(s.BatCurrent) pu.batteryCurrent.Set(s.BatVoltage * s.BatCurrent) pu.mainsCurrentIn.Set(s.InCurrent) @@ -128,7 +129,6 @@ func (pu *prometheusUpdater) updatePrometheus(newStatus *statusProcessed) { pu.mainsVoltageOut.Set(s.OutVoltage) pu.mainsPowerIn.Set(s.InVoltage * s.InCurrent) pu.mainsPowerOut.Set(s.OutVoltage * s.OutCurrent) - pu.mainsFreqIn.Set(s.InFreq) - pu.mainsFreqIn.Set(s.OutFreq) - + pu.mainsFreqIn.Set(s.InFrequency) + pu.mainsFreqOut.Set(s.OutFrequency) } diff --git a/webgui/webgui.go b/webgui/webgui.go index d7fa36f..2b2ad05 100644 --- a/webgui/webgui.go +++ b/webgui/webgui.go @@ -32,50 +32,28 @@ package webgui import ( "fmt" - "github.com/hpdvanwyk/invertergui/datasource" + "github.com/hpdvanwyk/invertergui/mk2if" "html/template" "net/http" "sync" "time" ) -const ( - Temperature = iota - Low_battery - Overload - Inverter - Float - Bulk - Absorption - Mains -) - -var leds = map[int]string{ - 0: "Temperature", - 1: "Low battery", - 2: "Overload", - 3: "Inverter", - 4: "Float", - 5: "Bulk", - 6: "Absorption", - 7: "Mains", -} - type WebGui struct { - respChan chan statusProcessed + respChan chan *mk2if.Mk2Info stopChan chan struct{} template *template.Template muninRespChan chan muninData - poller datasource.DataPoller + poller mk2if.Mk2If wg sync.WaitGroup pu *prometheusUpdater } -func NewWebGui(source datasource.DataPoller, batteryCapacity float64) *WebGui { +func NewWebGui(source mk2if.Mk2If) *WebGui { w := new(WebGui) - w.respChan = make(chan statusProcessed) + w.respChan = make(chan *mk2if.Mk2Info) w.muninRespChan = make(chan muninData) w.stopChan = make(chan struct{}) var err error @@ -87,12 +65,12 @@ func NewWebGui(source datasource.DataPoller, batteryCapacity float64) *WebGui { w.pu = newPrometheusUpdater() w.wg.Add(1) - go w.dataPoll(batteryCapacity) + go w.dataPoll() return w } type templateInput struct { - Error error + Error []error Date string @@ -111,7 +89,8 @@ type templateInput struct { BatPower string BatCharge string - InFreq string + InFreq string + OutFreq string Leds []string } @@ -119,7 +98,7 @@ type templateInput struct { func (w *WebGui) ServeHTTP(rw http.ResponseWriter, r *http.Request) { statusErr := <-w.respChan - tmpInput := buildTemplateInput(&statusErr, statusErr.timestamp) + tmpInput := buildTemplateInput(statusErr) err := w.template.Execute(rw, tmpInput) if err != nil { @@ -127,20 +106,28 @@ func (w *WebGui) ServeHTTP(rw http.ResponseWriter, r *http.Request) { } } -func buildTemplateInput(statusErr *statusProcessed, now time.Time) *templateInput { - status := statusErr.status +func ledName(nameInt int) string { + name, ok := mk2if.LedNames[nameInt] + if !ok { + return "Unknown led" + } + return name +} + +func buildTemplateInput(status *mk2if.Mk2Info) *templateInput { outPower := status.OutVoltage * status.OutCurrent inPower := status.InCurrent * status.InVoltage tmpInput := &templateInput{ - Error: statusErr.err, - Date: now.Format(time.RFC1123Z), + Error: status.Errors, + Date: status.Timestamp.Format(time.RFC1123Z), OutCurrent: fmt.Sprintf("%.3f", status.OutCurrent), OutVoltage: fmt.Sprintf("%.3f", status.OutVoltage), OutPower: fmt.Sprintf("%.3f", outPower), InCurrent: fmt.Sprintf("%.3f", status.InCurrent), InVoltage: fmt.Sprintf("%.3f", status.InVoltage), - InFreq: fmt.Sprintf("%.3f", status.InFreq), + InFreq: fmt.Sprintf("%.3f", status.InFrequency), + OutFreq: fmt.Sprintf("%.3f", status.OutFrequency), InPower: fmt.Sprintf("%.3f", inPower), InMinOut: fmt.Sprintf("%.3f", inPower-outPower), @@ -148,56 +135,33 @@ func buildTemplateInput(statusErr *statusProcessed, now time.Time) *templateInpu BatCurrent: fmt.Sprintf("%.3f", status.BatCurrent), BatVoltage: fmt.Sprintf("%.3f", status.BatVoltage), BatPower: fmt.Sprintf("%.3f", status.BatVoltage*status.BatCurrent), - BatCharge: fmt.Sprintf("%.3f", statusErr.chargeLevel), + BatCharge: fmt.Sprintf("%.3f", status.ChargeState*100), } - if len(status.Leds) == 8 { - for i := 7; i >= 0; i-- { - if status.Leds[i] == 1 { - tmpInput.Leds = append(tmpInput.Leds, leds[i]) - } - } + for i := range status.LedListOn { + tmpInput.Leds = append(tmpInput.Leds, ledName(status.LedListOn[i])) } return tmpInput } func (w *WebGui) Stop() { - w.poller.Stop() close(w.stopChan) w.wg.Wait() } -type statusProcessed struct { - status datasource.MultiplusStatus - chargeLevel float64 - err error - timestamp time.Time -} - // dataPoll waits for data from the w.poller channel. It will send its currently stored status // to respChan if anything reads from it. -func (w *WebGui) dataPoll(batteryCapacity float64) { - tracker := NewChargeTracker(batteryCapacity) +func (w *WebGui) dataPoll() { pollChan := w.poller.C() - var statusP statusProcessed var muninValues muninData + s := &mk2if.Mk2Info{} for { select { - case s := <-pollChan: - if s.Err != nil { - statusP.err = s.Err - } else { - statusP.status = s.MpStatus - statusP.err = nil - statusP.timestamp = s.Time - tracker.Update(s.MpStatus.BatCurrent, s.Time) - if s.MpStatus.Leds[Float] == 1 { - tracker.Reset() - } - statusP.chargeLevel = tracker.CurrentLevel() - calcMuninValues(&muninValues, &statusP) - w.pu.updatePrometheus(&statusP) + case s = <-pollChan: + if s.Valid { + calcMuninValues(&muninValues, s) + w.pu.updatePrometheus(s) } - case w.respChan <- statusP: + case w.respChan <- s: case w.muninRespChan <- muninValues: zeroMuninValues(&muninValues) case <-w.stopChan: diff --git a/webgui/webgui_test.go b/webgui/webgui_test.go index 09e55ac..dc7c019 100644 --- a/webgui/webgui_test.go +++ b/webgui/webgui_test.go @@ -31,57 +31,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package webgui import ( - "github.com/hpdvanwyk/invertergui/datasource" + "fmt" + "github.com/hpdvanwyk/invertergui/mk2if" "reflect" "testing" "time" ) -type mockSource struct { -} - -func NewMockSource() datasource.DataSource { - return &mockSource{} -} - -func (s *mockSource) GetData(status *datasource.MultiplusStatus) error { - status.OutCurrent = 2.0 - status.InCurrent = 2.3 - status.OutVoltage = 230.0 - status.InVoltage = 230.1 - status.BatVoltage = 25 - status.BatCurrent = -10 - status.InFreq = 50 - status.OutFreq = 50 - status.Leds = []int{0, 0, 0, 0, 1, 0, 0, 1} - return nil -} - func TestWebGui(t *testing.T) { t.Skip("Not yet implimented") //TODO figure out how to test template output. } type templateTest struct { - input *statusProcessed + input *mk2if.Mk2Info output *templateInput } var fakenow = time.Date(2017, 1, 2, 3, 4, 5, 6, time.UTC) var templateInputTests = []templateTest{ { - input: &statusProcessed{ - status: datasource.MultiplusStatus{ - OutCurrent: 2.0, - InCurrent: 2.3, - OutVoltage: 230.0, - InVoltage: 230.1, - BatVoltage: 25, - BatCurrent: -10, - InFreq: 50, - OutFreq: 50, - Leds: []int{0, 0, 0, 0, 1, 0, 0, 1}}, - err: nil, + input: &mk2if.Mk2Info{ + OutCurrent: 2.0, + InCurrent: 2.3, + OutVoltage: 230.0, + InVoltage: 230.1, + BatVoltage: 25, + BatCurrent: -10, + InFrequency: 50, + OutFrequency: 50, + ChargeState: 1, + LedListOn: []int{mk2if.LED_MAIN, mk2if.LED_FLOAT}, + Errors: nil, + Timestamp: fakenow, }, output: &templateInput{ Error: nil, @@ -97,16 +79,18 @@ var templateInputTests = []templateTest{ BatCurrent: "-10.000", BatPower: "-250.000", InFreq: "50.000", - BatCharge: "0.000", + OutFreq: "50.000", + BatCharge: "100.000", Leds: []string{"Mains", "Float"}}, }, } func TestTemplateInput(t *testing.T) { for i := range templateInputTests { - templateInput := buildTemplateInput(templateInputTests[i].input, fakenow) + templateInput := buildTemplateInput(templateInputTests[i].input) if !reflect.DeepEqual(templateInput, templateInputTests[i].output) { t.Errorf("buildTemplateInput not producing expected results") + fmt.Printf("%v\n%v\n", templateInput, templateInputTests[i].output) } } }