Use mk2if instead of datasource as data source.
This removes support for the python based mk2daemon.
This commit is contained in:
@@ -32,23 +32,54 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/hpdvanwyk/invertergui/datasource"
|
"github.com/hpdvanwyk/invertergui/mk2if"
|
||||||
"github.com/hpdvanwyk/invertergui/webgui"
|
"github.com/hpdvanwyk/invertergui/webgui"
|
||||||
|
"github.com/mikepb/go-serial"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
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.")
|
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()
|
flag.Parse()
|
||||||
|
|
||||||
source := datasource.NewJSONSource(*url)
|
var p io.ReadWriteCloser
|
||||||
poller := datasource.NewDataPoller(source, 10*time.Second)
|
var err error
|
||||||
gui := webgui.NewWebGui(poller, *capacity)
|
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("/", gui)
|
||||||
http.Handle("/munin", http.HandlerFunc(gui.ServeMuninHTTP))
|
http.Handle("/munin", http.HandlerFunc(gui.ServeMuninHTTP))
|
||||||
http.Handle("/muninconfig", http.HandlerFunc(gui.ServeMuninConfigHTTP))
|
http.Handle("/muninconfig", http.HandlerFunc(gui.ServeMuninConfigHTTP))
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
@@ -40,7 +40,11 @@ var htmlTemplate string = `<html>
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{if .Error}} <p>Error encountered: {{.Error}} </p> {{end}}
|
{{if .Error}} <p>Errors encountered: </p>
|
||||||
|
{{range .Error}}
|
||||||
|
<dt> {{.}}</dt>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
<dl>
|
<dl>
|
||||||
<dt> Date: {{.Date}}</dt>
|
<dt> Date: {{.Date}}</dt>
|
||||||
</dl>
|
</dl>
|
||||||
@@ -55,6 +59,7 @@ var htmlTemplate string = `<html>
|
|||||||
<dl>
|
<dl>
|
||||||
<dt> Output Current: {{.OutCurrent}} A</dt>
|
<dt> Output Current: {{.OutCurrent}} A</dt>
|
||||||
<dt> Output Voltage: {{.OutVoltage}} V</dt>
|
<dt> Output Voltage: {{.OutVoltage}} V</dt>
|
||||||
|
<dt> Output Frequency: {{.OutFreq}} Hz</dt>
|
||||||
<dt> Output Power: {{.OutPower}} VA</dt>
|
<dt> Output Power: {{.OutPower}} VA</dt>
|
||||||
</dl>
|
</dl>
|
||||||
<dl>
|
<dl>
|
||||||
@@ -68,7 +73,7 @@ var htmlTemplate string = `<html>
|
|||||||
<dt> Battery Current: {{.BatCurrent}} A</dt>
|
<dt> Battery Current: {{.BatCurrent}} A</dt>
|
||||||
<dt> Battery Voltage: {{.BatVoltage}} V</dt>
|
<dt> Battery Voltage: {{.BatVoltage}} V</dt>
|
||||||
<dt> Battery Power: {{.BatPower}} W</dt>
|
<dt> Battery Power: {{.BatPower}} W</dt>
|
||||||
<dt> Battery Charge: {{.BatCharge}} A h</dt>
|
<dt> Battery Charge: {{.BatCharge}} %</dt>
|
||||||
</dl>
|
</dl>
|
||||||
</body>
|
</body>
|
||||||
</html>`
|
</html>`
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ package webgui
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/hpdvanwyk/invertergui/mk2if"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type muninData struct {
|
type muninData struct {
|
||||||
statusP statusProcessed
|
status mk2if.Mk2Info
|
||||||
timesUpdated int
|
timesUpdated int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +51,8 @@ func (w *WebGui) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
calcMuninAverages(&muninDat)
|
calcMuninAverages(&muninDat)
|
||||||
|
|
||||||
statusP := &muninDat.statusP
|
status := muninDat.status
|
||||||
tmpInput := buildTemplateInput(statusP, time.Now())
|
tmpInput := buildTemplateInput(&status)
|
||||||
outputBuf := &bytes.Buffer{}
|
outputBuf := &bytes.Buffer{}
|
||||||
fmt.Fprintf(outputBuf, "multigraph in_batvolt\n")
|
fmt.Fprintf(outputBuf, "multigraph in_batvolt\n")
|
||||||
fmt.Fprintf(outputBuf, "volt.value %s\n", tmpInput.BatVoltage)
|
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, "powerin.value %s\n", tmpInput.InPower)
|
||||||
fmt.Fprintf(outputBuf, "powerout.value %s\n", tmpInput.OutPower)
|
fmt.Fprintf(outputBuf, "powerout.value %s\n", tmpInput.OutPower)
|
||||||
fmt.Fprintf(outputBuf, "multigraph in_mainsfreq\n")
|
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()))
|
_, err := rw.Write([]byte(outputBuf.String()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,12 +93,12 @@ volt.label Voltage of battery (V)
|
|||||||
|
|
||||||
multigraph in_batcharge
|
multigraph in_batcharge
|
||||||
graph_title Battery Charge
|
graph_title Battery Charge
|
||||||
graph_vlabel Charge (A h)
|
graph_vlabel Charge (%)
|
||||||
graph_category inverter
|
graph_category inverter
|
||||||
graph_info Battery charge
|
graph_info Battery charge
|
||||||
|
|
||||||
charge.info Estimated charge of battery
|
charge.info Estimated charge of battery
|
||||||
charge.label Battery charge (A h)
|
charge.label Battery charge (%)
|
||||||
|
|
||||||
multigraph in_batcurrent
|
multigraph in_batcurrent
|
||||||
graph_title Battery Current
|
graph_title Battery Current
|
||||||
@@ -156,8 +157,10 @@ graph_vlabel Frequency (Hz)
|
|||||||
graph_category inverter
|
graph_category inverter
|
||||||
graph_info Mains frequency
|
graph_info Mains frequency
|
||||||
|
|
||||||
freq.info Input frequency
|
freqin.info In frequency
|
||||||
freq.label Input frequency (Hz)
|
freqin.label In frequency (Hz)
|
||||||
|
freqout.info Out frequency
|
||||||
|
freqout.label Out frequency (Hz)
|
||||||
`
|
`
|
||||||
|
|
||||||
_, err := rw.Write([]byte(output))
|
_, 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.
|
//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
|
muninDat.timesUpdated += 1
|
||||||
muninVal := &muninDat.statusP
|
muninVal := &muninDat.status
|
||||||
muninVal.status.OutCurrent += newStatus.status.OutCurrent
|
muninVal.OutCurrent += newStatus.OutCurrent
|
||||||
muninVal.status.InCurrent += newStatus.status.InCurrent
|
muninVal.InCurrent += newStatus.InCurrent
|
||||||
muninVal.status.BatCurrent += newStatus.status.BatCurrent
|
muninVal.BatCurrent += newStatus.BatCurrent
|
||||||
|
|
||||||
muninVal.status.OutVoltage += newStatus.status.OutVoltage
|
muninVal.OutVoltage += newStatus.OutVoltage
|
||||||
muninVal.status.InVoltage += newStatus.status.InVoltage
|
muninVal.InVoltage += newStatus.InVoltage
|
||||||
muninVal.status.BatVoltage += newStatus.status.BatVoltage
|
muninVal.BatVoltage += newStatus.BatVoltage
|
||||||
|
|
||||||
muninVal.status.InFreq = newStatus.status.InFreq
|
muninVal.InFrequency = newStatus.InFrequency
|
||||||
|
muninVal.OutFrequency = newStatus.OutFrequency
|
||||||
|
|
||||||
muninVal.chargeLevel = newStatus.chargeLevel
|
muninVal.ChargeState = newStatus.ChargeState
|
||||||
muninVal.status.Leds = newStatus.status.Leds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func calcMuninAverages(muninDat *muninData) {
|
func calcMuninAverages(muninDat *muninData) {
|
||||||
muninVal := &muninDat.statusP
|
muninVal := &muninDat.status
|
||||||
muninVal.status.OutCurrent /= float64(muninDat.timesUpdated)
|
muninVal.OutCurrent /= float64(muninDat.timesUpdated)
|
||||||
muninVal.status.InCurrent /= float64(muninDat.timesUpdated)
|
muninVal.InCurrent /= float64(muninDat.timesUpdated)
|
||||||
muninVal.status.BatCurrent /= float64(muninDat.timesUpdated)
|
muninVal.BatCurrent /= float64(muninDat.timesUpdated)
|
||||||
|
|
||||||
muninVal.status.OutVoltage /= float64(muninDat.timesUpdated)
|
muninVal.OutVoltage /= float64(muninDat.timesUpdated)
|
||||||
muninVal.status.InVoltage /= float64(muninDat.timesUpdated)
|
muninVal.InVoltage /= float64(muninDat.timesUpdated)
|
||||||
muninVal.status.BatVoltage /= float64(muninDat.timesUpdated)
|
muninVal.BatVoltage /= float64(muninDat.timesUpdated)
|
||||||
}
|
}
|
||||||
|
|
||||||
func zeroMuninValues(muninDat *muninData) {
|
func zeroMuninValues(muninDat *muninData) {
|
||||||
muninDat.timesUpdated = 0
|
muninDat.timesUpdated = 0
|
||||||
muninVal := &muninDat.statusP
|
muninVal := &muninDat.status
|
||||||
muninVal.status.OutCurrent = 0
|
muninVal.OutCurrent = 0
|
||||||
muninVal.status.InCurrent = 0
|
muninVal.InCurrent = 0
|
||||||
muninVal.status.BatCurrent = 0
|
muninVal.BatCurrent = 0
|
||||||
|
|
||||||
muninVal.status.OutVoltage = 0
|
muninVal.OutVoltage = 0
|
||||||
muninVal.status.InVoltage = 0
|
muninVal.InVoltage = 0
|
||||||
muninVal.status.BatVoltage = 0
|
muninVal.BatVoltage = 0
|
||||||
|
|
||||||
muninVal.status.InFreq = 0
|
muninVal.InFrequency = 0
|
||||||
|
muninVal.OutFrequency = 0
|
||||||
|
|
||||||
muninVal.chargeLevel = 0
|
muninVal.ChargeState = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
package webgui
|
package webgui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hpdvanwyk/invertergui/mk2if"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -116,10 +117,10 @@ func newPrometheusUpdater() *prometheusUpdater {
|
|||||||
return tmp
|
return tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pu *prometheusUpdater) updatePrometheus(newStatus *statusProcessed) {
|
func (pu *prometheusUpdater) updatePrometheus(newStatus *mk2if.Mk2Info) {
|
||||||
s := newStatus.status
|
s := newStatus
|
||||||
pu.batteryVoltage.Set(s.BatVoltage)
|
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.BatCurrent)
|
||||||
pu.batteryCurrent.Set(s.BatVoltage * s.BatCurrent)
|
pu.batteryCurrent.Set(s.BatVoltage * s.BatCurrent)
|
||||||
pu.mainsCurrentIn.Set(s.InCurrent)
|
pu.mainsCurrentIn.Set(s.InCurrent)
|
||||||
@@ -128,7 +129,6 @@ func (pu *prometheusUpdater) updatePrometheus(newStatus *statusProcessed) {
|
|||||||
pu.mainsVoltageOut.Set(s.OutVoltage)
|
pu.mainsVoltageOut.Set(s.OutVoltage)
|
||||||
pu.mainsPowerIn.Set(s.InVoltage * s.InCurrent)
|
pu.mainsPowerIn.Set(s.InVoltage * s.InCurrent)
|
||||||
pu.mainsPowerOut.Set(s.OutVoltage * s.OutCurrent)
|
pu.mainsPowerOut.Set(s.OutVoltage * s.OutCurrent)
|
||||||
pu.mainsFreqIn.Set(s.InFreq)
|
pu.mainsFreqIn.Set(s.InFrequency)
|
||||||
pu.mainsFreqIn.Set(s.OutFreq)
|
pu.mainsFreqOut.Set(s.OutFrequency)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
100
webgui/webgui.go
100
webgui/webgui.go
@@ -32,50 +32,28 @@ package webgui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hpdvanwyk/invertergui/datasource"
|
"github.com/hpdvanwyk/invertergui/mk2if"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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 {
|
type WebGui struct {
|
||||||
respChan chan statusProcessed
|
respChan chan *mk2if.Mk2Info
|
||||||
stopChan chan struct{}
|
stopChan chan struct{}
|
||||||
template *template.Template
|
template *template.Template
|
||||||
|
|
||||||
muninRespChan chan muninData
|
muninRespChan chan muninData
|
||||||
poller datasource.DataPoller
|
poller mk2if.Mk2If
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
pu *prometheusUpdater
|
pu *prometheusUpdater
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWebGui(source datasource.DataPoller, batteryCapacity float64) *WebGui {
|
func NewWebGui(source mk2if.Mk2If) *WebGui {
|
||||||
w := new(WebGui)
|
w := new(WebGui)
|
||||||
w.respChan = make(chan statusProcessed)
|
w.respChan = make(chan *mk2if.Mk2Info)
|
||||||
w.muninRespChan = make(chan muninData)
|
w.muninRespChan = make(chan muninData)
|
||||||
w.stopChan = make(chan struct{})
|
w.stopChan = make(chan struct{})
|
||||||
var err error
|
var err error
|
||||||
@@ -87,12 +65,12 @@ func NewWebGui(source datasource.DataPoller, batteryCapacity float64) *WebGui {
|
|||||||
w.pu = newPrometheusUpdater()
|
w.pu = newPrometheusUpdater()
|
||||||
|
|
||||||
w.wg.Add(1)
|
w.wg.Add(1)
|
||||||
go w.dataPoll(batteryCapacity)
|
go w.dataPoll()
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
type templateInput struct {
|
type templateInput struct {
|
||||||
Error error
|
Error []error
|
||||||
|
|
||||||
Date string
|
Date string
|
||||||
|
|
||||||
@@ -112,6 +90,7 @@ type templateInput struct {
|
|||||||
BatCharge string
|
BatCharge string
|
||||||
|
|
||||||
InFreq string
|
InFreq string
|
||||||
|
OutFreq string
|
||||||
|
|
||||||
Leds []string
|
Leds []string
|
||||||
}
|
}
|
||||||
@@ -119,7 +98,7 @@ type templateInput struct {
|
|||||||
func (w *WebGui) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
func (w *WebGui) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
statusErr := <-w.respChan
|
statusErr := <-w.respChan
|
||||||
|
|
||||||
tmpInput := buildTemplateInput(&statusErr, statusErr.timestamp)
|
tmpInput := buildTemplateInput(statusErr)
|
||||||
|
|
||||||
err := w.template.Execute(rw, tmpInput)
|
err := w.template.Execute(rw, tmpInput)
|
||||||
if err != nil {
|
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 {
|
func ledName(nameInt int) string {
|
||||||
status := statusErr.status
|
name, ok := mk2if.LedNames[nameInt]
|
||||||
|
if !ok {
|
||||||
|
return "Unknown led"
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTemplateInput(status *mk2if.Mk2Info) *templateInput {
|
||||||
outPower := status.OutVoltage * status.OutCurrent
|
outPower := status.OutVoltage * status.OutCurrent
|
||||||
inPower := status.InCurrent * status.InVoltage
|
inPower := status.InCurrent * status.InVoltage
|
||||||
|
|
||||||
tmpInput := &templateInput{
|
tmpInput := &templateInput{
|
||||||
Error: statusErr.err,
|
Error: status.Errors,
|
||||||
Date: now.Format(time.RFC1123Z),
|
Date: status.Timestamp.Format(time.RFC1123Z),
|
||||||
OutCurrent: fmt.Sprintf("%.3f", status.OutCurrent),
|
OutCurrent: fmt.Sprintf("%.3f", status.OutCurrent),
|
||||||
OutVoltage: fmt.Sprintf("%.3f", status.OutVoltage),
|
OutVoltage: fmt.Sprintf("%.3f", status.OutVoltage),
|
||||||
OutPower: fmt.Sprintf("%.3f", outPower),
|
OutPower: fmt.Sprintf("%.3f", outPower),
|
||||||
InCurrent: fmt.Sprintf("%.3f", status.InCurrent),
|
InCurrent: fmt.Sprintf("%.3f", status.InCurrent),
|
||||||
InVoltage: fmt.Sprintf("%.3f", status.InVoltage),
|
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),
|
InPower: fmt.Sprintf("%.3f", inPower),
|
||||||
|
|
||||||
InMinOut: fmt.Sprintf("%.3f", inPower-outPower),
|
InMinOut: fmt.Sprintf("%.3f", inPower-outPower),
|
||||||
@@ -148,56 +135,33 @@ func buildTemplateInput(statusErr *statusProcessed, now time.Time) *templateInpu
|
|||||||
BatCurrent: fmt.Sprintf("%.3f", status.BatCurrent),
|
BatCurrent: fmt.Sprintf("%.3f", status.BatCurrent),
|
||||||
BatVoltage: fmt.Sprintf("%.3f", status.BatVoltage),
|
BatVoltage: fmt.Sprintf("%.3f", status.BatVoltage),
|
||||||
BatPower: fmt.Sprintf("%.3f", status.BatVoltage*status.BatCurrent),
|
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
|
return tmpInput
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WebGui) Stop() {
|
func (w *WebGui) Stop() {
|
||||||
w.poller.Stop()
|
|
||||||
close(w.stopChan)
|
close(w.stopChan)
|
||||||
w.wg.Wait()
|
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
|
// dataPoll waits for data from the w.poller channel. It will send its currently stored status
|
||||||
// to respChan if anything reads from it.
|
// to respChan if anything reads from it.
|
||||||
func (w *WebGui) dataPoll(batteryCapacity float64) {
|
func (w *WebGui) dataPoll() {
|
||||||
tracker := NewChargeTracker(batteryCapacity)
|
|
||||||
pollChan := w.poller.C()
|
pollChan := w.poller.C()
|
||||||
var statusP statusProcessed
|
|
||||||
var muninValues muninData
|
var muninValues muninData
|
||||||
|
s := &mk2if.Mk2Info{}
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case s := <-pollChan:
|
case s = <-pollChan:
|
||||||
if s.Err != nil {
|
if s.Valid {
|
||||||
statusP.err = s.Err
|
calcMuninValues(&muninValues, s)
|
||||||
} else {
|
w.pu.updatePrometheus(s)
|
||||||
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()
|
case w.respChan <- s:
|
||||||
calcMuninValues(&muninValues, &statusP)
|
|
||||||
w.pu.updatePrometheus(&statusP)
|
|
||||||
}
|
|
||||||
case w.respChan <- statusP:
|
|
||||||
case w.muninRespChan <- muninValues:
|
case w.muninRespChan <- muninValues:
|
||||||
zeroMuninValues(&muninValues)
|
zeroMuninValues(&muninValues)
|
||||||
case <-w.stopChan:
|
case <-w.stopChan:
|
||||||
|
|||||||
@@ -31,57 +31,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
package webgui
|
package webgui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hpdvanwyk/invertergui/datasource"
|
"fmt"
|
||||||
|
"github.com/hpdvanwyk/invertergui/mk2if"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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) {
|
func TestWebGui(t *testing.T) {
|
||||||
t.Skip("Not yet implimented")
|
t.Skip("Not yet implimented")
|
||||||
//TODO figure out how to test template output.
|
//TODO figure out how to test template output.
|
||||||
}
|
}
|
||||||
|
|
||||||
type templateTest struct {
|
type templateTest struct {
|
||||||
input *statusProcessed
|
input *mk2if.Mk2Info
|
||||||
output *templateInput
|
output *templateInput
|
||||||
}
|
}
|
||||||
|
|
||||||
var fakenow = time.Date(2017, 1, 2, 3, 4, 5, 6, time.UTC)
|
var fakenow = time.Date(2017, 1, 2, 3, 4, 5, 6, time.UTC)
|
||||||
var templateInputTests = []templateTest{
|
var templateInputTests = []templateTest{
|
||||||
{
|
{
|
||||||
input: &statusProcessed{
|
input: &mk2if.Mk2Info{
|
||||||
status: datasource.MultiplusStatus{
|
|
||||||
OutCurrent: 2.0,
|
OutCurrent: 2.0,
|
||||||
InCurrent: 2.3,
|
InCurrent: 2.3,
|
||||||
OutVoltage: 230.0,
|
OutVoltage: 230.0,
|
||||||
InVoltage: 230.1,
|
InVoltage: 230.1,
|
||||||
BatVoltage: 25,
|
BatVoltage: 25,
|
||||||
BatCurrent: -10,
|
BatCurrent: -10,
|
||||||
InFreq: 50,
|
InFrequency: 50,
|
||||||
OutFreq: 50,
|
OutFrequency: 50,
|
||||||
Leds: []int{0, 0, 0, 0, 1, 0, 0, 1}},
|
ChargeState: 1,
|
||||||
err: nil,
|
LedListOn: []int{mk2if.LED_MAIN, mk2if.LED_FLOAT},
|
||||||
|
Errors: nil,
|
||||||
|
Timestamp: fakenow,
|
||||||
},
|
},
|
||||||
output: &templateInput{
|
output: &templateInput{
|
||||||
Error: nil,
|
Error: nil,
|
||||||
@@ -97,16 +79,18 @@ var templateInputTests = []templateTest{
|
|||||||
BatCurrent: "-10.000",
|
BatCurrent: "-10.000",
|
||||||
BatPower: "-250.000",
|
BatPower: "-250.000",
|
||||||
InFreq: "50.000",
|
InFreq: "50.000",
|
||||||
BatCharge: "0.000",
|
OutFreq: "50.000",
|
||||||
|
BatCharge: "100.000",
|
||||||
Leds: []string{"Mains", "Float"}},
|
Leds: []string{"Mains", "Float"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTemplateInput(t *testing.T) {
|
func TestTemplateInput(t *testing.T) {
|
||||||
for i := range templateInputTests {
|
for i := range templateInputTests {
|
||||||
templateInput := buildTemplateInput(templateInputTests[i].input, fakenow)
|
templateInput := buildTemplateInput(templateInputTests[i].input)
|
||||||
if !reflect.DeepEqual(templateInput, templateInputTests[i].output) {
|
if !reflect.DeepEqual(templateInput, templateInputTests[i].output) {
|
||||||
t.Errorf("buildTemplateInput not producing expected results")
|
t.Errorf("buildTemplateInput not producing expected results")
|
||||||
|
fmt.Printf("%v\n%v\n", templateInput, templateInputTests[i].output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user