Use mk2if instead of datasource as data source.
This removes support for the python based mk2daemon.
This commit is contained in:
@@ -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>`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
102
webgui/webgui.go
102
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:
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user