Use mk2if instead of datasource as data source.

This removes support for the python based mk2daemon.
This commit is contained in:
Hendrik van Wyk
2017-09-16 15:37:58 +02:00
parent dd8ee443e2
commit a52af88bff
10 changed files with 144 additions and 492 deletions

View File

@@ -40,7 +40,11 @@ var htmlTemplate string = `<html>
</head>
<body>
{{if .Error}} <p>Error encountered: {{.Error}} </p> {{end}}
{{if .Error}} <p>Errors encountered: </p>
{{range .Error}}
<dt> {{.}}</dt>
{{end}}
{{end}}
<dl>
<dt> Date: {{.Date}}</dt>
</dl>
@@ -55,6 +59,7 @@ var htmlTemplate string = `<html>
<dl>
<dt> Output Current: {{.OutCurrent}} A</dt>
<dt> Output Voltage: {{.OutVoltage}} V</dt>
<dt> Output Frequency: {{.OutFreq}} Hz</dt>
<dt> Output Power: {{.OutPower}} VA</dt>
</dl>
<dl>
@@ -68,7 +73,7 @@ var htmlTemplate string = `<html>
<dt> Battery Current: {{.BatCurrent}} A</dt>
<dt> Battery Voltage: {{.BatVoltage}} V</dt>
<dt> Battery Power: {{.BatPower}} W</dt>
<dt> Battery Charge: {{.BatCharge}} A h</dt>
<dt> Battery Charge: {{.BatCharge}} %</dt>
</dl>
</body>
</html>`

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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:

View File

@@ -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)
}
}
}