Concept plugin layout

This commit is contained in:
Nicholas Thompson
2019-03-10 21:36:34 +02:00
committed by ncthompson
parent d02de285d9
commit 47e73a4eff
12 changed files with 363 additions and 287 deletions

View File

@@ -26,21 +26,13 @@
#OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #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. #OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.PHONY: test test-race vet install gofmt docker statik lint clean guimock invertergui invertercli build .PHONY: test test-race vet install gofmt docker statik lint clean invertergui
.DEFAULT_GOAL = build .DEFAULT_GOAL = invertergui
guimock:
go build ./cmd/guimock/
invertergui: invertergui:
go build ./cmd/invertergui/ go build ./cmd/invertergui/
invertercli:
go build ./cmd/invertercli/
build: guimock invertergui invertercli
all: build gofmt test all: build gofmt test
gofmt: gofmt:
@@ -62,4 +54,4 @@ lint:
golangci-lint run golangci-lint run
clean: clean:
rm ./guimock ./invertercli ./invertergui rm ./invertergui

View File

@@ -1,29 +0,0 @@
package main
import (
"flag"
"log"
"net/http"
"github.com/diebietse/invertergui/frontend"
"github.com/diebietse/invertergui/mk2driver"
"github.com/diebietse/invertergui/webgui"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
addr := flag.String("addr", ":8080", "TCP address to listen on.")
flag.Parse()
mk2 := mk2driver.NewMk2Mock()
gui := webgui.NewWebGui(mk2)
http.Handle("/", frontend.NewStatic())
http.Handle("/ws", http.HandlerFunc(gui.ServeHub))
http.Handle("/munin", http.HandlerFunc(gui.ServeMuninHTTP))
http.Handle("/muninconfig", http.HandlerFunc(gui.ServeMuninConfigHTTP))
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(*addr, nil))
}

View File

@@ -1,90 +0,0 @@
package main
import (
"flag"
"fmt"
"io"
"log"
"net"
"os"
"os/signal"
"syscall"
"github.com/diebietse/invertergui/mk2if"
"github.com/tarm/serial"
"github.com/diebietse/invertergui/mk2driver"
"github.com/mikepb/go-serial"
)
// Basic CLI to serve as example lib usage
func main() {
//Info = log.New()
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()
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 {
serialConfig := &serial.Config{Name: *dev, Baud: 2400}
p, err = serial.OpenPort(serialConfig)
if err != nil {
panic(err)
}
}
defer p.Close()
mk2, err := mk2driver.NewMk2Connection(p)
if err != nil {
panic(err)
}
defer mk2.Close()
c := mk2.C()
sigterm := make(chan os.Signal, 1)
signal.Notify(sigterm, syscall.SIGTERM, os.Interrupt)
mainloop:
for {
select {
case tmp := <-c:
if tmp.Valid {
PrintInfo(tmp)
}
case <-sigterm:
break mainloop
}
}
log.Printf("Closing connection")
}
func PrintInfo(info *mk2driver.Mk2Info) {
out := fmt.Sprintf("Version: %v\n", info.Version)
out += fmt.Sprintf("Bat Volt: %.2fV Bat Cur: %.2fA \n", info.BatVoltage, info.BatCurrent)
out += fmt.Sprintf("In Volt: %.2fV In Cur: %.2fA In Freq %.2fHz\n", info.InVoltage, info.InCurrent, info.InFrequency)
out += fmt.Sprintf("Out Volt: %.2fV Out Cur: %.2fA Out Freq %.2fHz\n", info.OutVoltage, info.OutCurrent, info.OutFrequency)
out += fmt.Sprintf("In Power %.2fW Out Power %.2fW\n", info.InVoltage*info.InCurrent, info.OutVoltage*info.OutCurrent)
out += fmt.Sprintf("Charge State: %.2f%%\n", info.ChargeState*100)
out += "LEDs state:"
for k, v := range info.LEDs {
out += fmt.Sprintf(" %s %s", mk2driver.LedNames[k], mk2driver.StateNames[v])
}
out += "\nErrors:"
for _, v := range info.Errors {
out += " " + v.Error()
}
out += "\n"
log.Printf("System Info: \n%v", out)
}

View File

@@ -38,8 +38,12 @@ import (
"net/http" "net/http"
"github.com/diebietse/invertergui/frontend" "github.com/diebietse/invertergui/frontend"
"github.com/diebietse/invertergui/mk2core"
"github.com/diebietse/invertergui/mk2driver" "github.com/diebietse/invertergui/mk2driver"
"github.com/diebietse/invertergui/webgui" "github.com/diebietse/invertergui/plugins/cli"
"github.com/diebietse/invertergui/plugins/munin"
"github.com/diebietse/invertergui/plugins/prometheus"
"github.com/diebietse/invertergui/plugins/webui"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/tarm/serial" "github.com/tarm/serial"
) )
@@ -50,14 +54,44 @@ func main() {
tcp := flag.Bool("tcp", false, "Use TCP instead of TTY") tcp := flag.Bool("tcp", false, "Use TCP instead of TTY")
ip := flag.String("ip", "localhost:8139", "IP to connect when using tcp connection.") ip := flag.String("ip", "localhost:8139", "IP to connect when using tcp connection.")
dev := flag.String("dev", "/dev/ttyUSB0", "TTY device to use.") dev := flag.String("dev", "/dev/ttyUSB0", "TTY device to use.")
mock := flag.Bool("mock", false, "Creates a mock device for test puposes")
cliEnable := flag.Bool("cli", false, "Enable CLI output")
flag.Parse() flag.Parse()
var mk2 mk2driver.Mk2
if *mock {
mk2 = mk2driver.NewMk2Mock()
} else {
mk2 = getMk2Device(*tcp, *ip, *dev)
}
defer mk2.Close()
core := mk2core.NewCore(mk2)
if *cliEnable {
cli.NewCli(core.NewSubscription())
}
gui := webui.NewWebGui(core.NewSubscription())
mu := munin.NewMunin(core.NewSubscription())
prometheus.NewPrometheus(core.NewSubscription())
http.Handle("/", frontend.NewStatic())
http.Handle("/ws", http.HandlerFunc(gui.ServeHub))
http.Handle("/munin", http.HandlerFunc(mu.ServeMuninHTTP))
http.Handle("/muninconfig", http.HandlerFunc(mu.ServeMuninConfigHTTP))
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(*addr, nil))
}
func getMk2Device(tcp bool, ip, dev string) mk2driver.Mk2 {
var p io.ReadWriteCloser var p io.ReadWriteCloser
var err error var err error
var tcpAddr *net.TCPAddr var tcpAddr *net.TCPAddr
if *tcp { if tcp {
tcpAddr, err = net.ResolveTCPAddr("tcp", *ip) tcpAddr, err = net.ResolveTCPAddr("tcp", ip)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -66,25 +100,16 @@ func main() {
panic(err) panic(err)
} }
} else { } else {
serialConfig := &serial.Config{Name: *dev, Baud: 2400} serialConfig := &serial.Config{Name: dev, Baud: 2400}
p, err = serial.OpenPort(serialConfig) p, err = serial.OpenPort(serialConfig)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
defer p.Close()
mk2, err := mk2driver.NewMk2Connection(p) mk2, err := mk2driver.NewMk2Connection(p)
if err != nil { if err != nil {
panic(err) panic(err)
} }
defer mk2.Close()
gui := webgui.NewWebGui(mk2) return mk2
http.Handle("/", frontend.NewStatic())
http.Handle("/ws", http.HandlerFunc(gui.ServeHub))
http.Handle("/munin", http.HandlerFunc(gui.ServeMuninHTTP))
http.Handle("/muninconfig", http.HandlerFunc(gui.ServeMuninConfigHTTP))
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(*addr, nil))
} }

57
mk2core/core.go Normal file
View File

@@ -0,0 +1,57 @@
package mk2core
import (
"github.com/diebietse/invertergui/mk2driver"
)
type Core struct {
mk2driver.Mk2
plugins map[*subscription]bool
register chan *subscription
}
func NewCore(m mk2driver.Mk2) *Core {
core := &Core{
Mk2: m,
register: make(chan *subscription, 255),
plugins: map[*subscription]bool{},
}
go core.run()
return core
}
func (c *Core) NewSubscription() mk2driver.Mk2 {
sub := &subscription{
send: make(chan *mk2driver.Mk2Info),
}
c.register <- sub
return sub
}
func (c *Core) run() {
for {
select {
case r := <-c.register:
c.plugins[r] = true
case e := <-c.C():
for plugin := range c.plugins {
select {
case plugin.send <- e:
default:
}
}
}
}
}
type subscription struct {
send chan *mk2driver.Mk2Info
}
func (s *subscription) C() chan *mk2driver.Mk2Info {
return s.send
}
func (s *subscription) Close() {
close(s.send)
}

View File

@@ -1,7 +1,6 @@
package mk2driver package mk2driver
import ( import (
"fmt"
"time" "time"
) )
@@ -64,7 +63,6 @@ func (m *mock) genMockValues() {
if mult < 0 { if mult < 0 {
mult = 1.0 mult = 1.0
} }
fmt.Printf("Sending\n")
m.c <- input m.c <- input
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }

47
plugins/cli/cli.go Normal file
View File

@@ -0,0 +1,47 @@
package cli
import (
"fmt"
"log"
"github.com/diebietse/invertergui/mk2driver"
)
type Cli struct {
mk2driver.Mk2
}
func NewCli(mk2 mk2driver.Mk2) {
newCli := &Cli{
Mk2: mk2,
}
go newCli.run()
}
func (c *Cli) run() {
for e := range c.C() {
if e.Valid {
printInfo(e)
}
}
}
func printInfo(info *mk2driver.Mk2Info) {
out := fmt.Sprintf("Version: %v\n", info.Version)
out += fmt.Sprintf("Bat Volt: %.2fV Bat Cur: %.2fA \n", info.BatVoltage, info.BatCurrent)
out += fmt.Sprintf("In Volt: %.2fV In Cur: %.2fA In Freq %.2fHz\n", info.InVoltage, info.InCurrent, info.InFrequency)
out += fmt.Sprintf("Out Volt: %.2fV Out Cur: %.2fA Out Freq %.2fHz\n", info.OutVoltage, info.OutCurrent, info.OutFrequency)
out += fmt.Sprintf("In Power %.2fW Out Power %.2fW\n", info.InVoltage*info.InCurrent, info.OutVoltage*info.OutCurrent)
out += fmt.Sprintf("Charge State: %.2f%%\n", info.ChargeState*100)
out += "LEDs state:"
for k, v := range info.LEDs {
out += fmt.Sprintf(" %s %s", mk2driver.LedNames[k], mk2driver.StateNames[v])
}
out += "\nErrors:"
for _, v := range info.Errors {
out += " " + v.Error()
}
out += "\n"
log.Printf("System Info: \n%v", out)
}

View File

@@ -28,32 +28,49 @@ 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. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package webgui package munin
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"net/http" "net/http"
"time"
"github.com/diebietse/invertergui/mk2driver" "github.com/diebietse/invertergui/mk2driver"
) )
type Munin struct {
mk2driver.Mk2
muninResponse chan *muninData
}
type muninData struct { type muninData struct {
status mk2driver.Mk2Info status *mk2driver.Mk2Info
timesUpdated int timesUpdated int
} }
func (w *WebGui) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) { func NewMunin(mk2 mk2driver.Mk2) *Munin {
muninDat := <-w.muninRespChan m := &Munin{
Mk2: mk2,
muninResponse: make(chan *muninData),
}
go m.run()
return m
}
func (m *Munin) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) {
muninDat := <-m.muninResponse
if muninDat.timesUpdated == 0 { if muninDat.timesUpdated == 0 {
rw.WriteHeader(500) rw.WriteHeader(500)
_, _ = rw.Write([]byte("No data to return.\n")) _, _ = rw.Write([]byte("No data to return.\n"))
return return
} }
calcMuninAverages(&muninDat) calcMuninAverages(muninDat)
status := muninDat.status status := muninDat.status
tmpInput := buildTemplateInput(&status) 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)
@@ -82,98 +99,35 @@ func (w *WebGui) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) {
} }
} }
func (w *WebGui) ServeMuninConfigHTTP(rw http.ResponseWriter, r *http.Request) { func (m *Munin) ServeMuninConfigHTTP(rw http.ResponseWriter, r *http.Request) {
output := `multigraph in_batvolt output := muninConfig
graph_title Battery Voltage
graph_vlabel Voltage (V)
graph_category inverter
graph_info Battery voltage
volt.info Voltage of battery
volt.label Voltage of battery (V)
multigraph in_batcharge
graph_title Battery Charge
graph_vlabel Charge (%)
graph_category inverter
graph_info Battery charge
charge.info Estimated charge of battery
charge.label Battery charge (%)
multigraph in_batcurrent
graph_title Battery Current
graph_vlabel Current (A)
graph_category inverter
graph_info Battery current
current.info Battery current
current.label Battery current (A)
multigraph in_batpower
graph_title Battery Power
graph_vlabel Power (W)
graph_category inverter
graph_info Battery power
power.info Battery power
power.label Battery power (W)
multigraph in_mainscurrent
graph_title Mains Current
graph_vlabel Current (A)
graph_category inverter
graph_info Mains current
currentin.info Input current
currentin.label Input current (A)
currentout.info Output current
currentout.label Output current (A)
multigraph in_mainsvoltage
graph_title Mains Voltage
graph_vlabel Voltage (V)
graph_category inverter
graph_info Mains voltage
voltagein.info Input voltage
voltagein.label Input voltage (V)
voltageout.info Output voltage
voltageout.label Output voltage (V)
multigraph in_mainspower
graph_title Mains Power
graph_vlabel Power (VA)
graph_category inverter
graph_info Mains power
powerin.info Input power
powerin.label Input power (VA)
powerout.info Output power
powerout.label Output power (VA)
multigraph in_mainsfreq
graph_title Mains frequency
graph_vlabel Frequency (Hz)
graph_category inverter
graph_info Mains frequency
freqin.info In frequency
freqin.label In frequency (Hz)
freqout.info Out frequency
freqout.label Out frequency (Hz)
`
_, err := rw.Write([]byte(output)) _, err := rw.Write([]byte(output))
if err != nil { if err != nil {
fmt.Printf("%v\n", err) fmt.Printf("%v\n", err)
} }
} }
func (m *Munin) run() {
muninValues := &muninData{
status: &mk2driver.Mk2Info{},
}
for {
select {
case e := <-m.C():
if e.Valid {
calcMuninValues(muninValues, e)
}
case m.muninResponse <- muninValues:
zeroMuninValues(muninValues)
}
}
}
//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 *mk2driver.Mk2Info) { func calcMuninValues(muninDat *muninData, newStatus *mk2driver.Mk2Info) {
muninDat.timesUpdated++ muninDat.timesUpdated++
muninVal := &muninDat.status muninVal := muninDat.status
muninVal.OutCurrent += newStatus.OutCurrent muninVal.OutCurrent += newStatus.OutCurrent
muninVal.InCurrent += newStatus.InCurrent muninVal.InCurrent += newStatus.InCurrent
muninVal.BatCurrent += newStatus.BatCurrent muninVal.BatCurrent += newStatus.BatCurrent
@@ -189,7 +143,7 @@ func calcMuninValues(muninDat *muninData, newStatus *mk2driver.Mk2Info) {
} }
func calcMuninAverages(muninDat *muninData) { func calcMuninAverages(muninDat *muninData) {
muninVal := &muninDat.status muninVal := muninDat.status
muninVal.OutCurrent /= float64(muninDat.timesUpdated) muninVal.OutCurrent /= float64(muninDat.timesUpdated)
muninVal.InCurrent /= float64(muninDat.timesUpdated) muninVal.InCurrent /= float64(muninDat.timesUpdated)
muninVal.BatCurrent /= float64(muninDat.timesUpdated) muninVal.BatCurrent /= float64(muninDat.timesUpdated)
@@ -201,7 +155,7 @@ func calcMuninAverages(muninDat *muninData) {
func zeroMuninValues(muninDat *muninData) { func zeroMuninValues(muninDat *muninData) {
muninDat.timesUpdated = 0 muninDat.timesUpdated = 0
muninVal := &muninDat.status muninVal := muninDat.status
muninVal.OutCurrent = 0 muninVal.OutCurrent = 0
muninVal.InCurrent = 0 muninVal.InCurrent = 0
muninVal.BatCurrent = 0 muninVal.BatCurrent = 0
@@ -215,3 +169,50 @@ func zeroMuninValues(muninDat *muninData) {
muninVal.ChargeState = 0 muninVal.ChargeState = 0
} }
type templateInput struct {
Date string `json:"date"`
OutCurrent string `json:"output_current"`
OutVoltage string `json:"output_voltage"`
OutPower string `json:"output_power"`
InCurrent string `json:"input_current"`
InVoltage string `json:"input_voltage"`
InPower string `json:"input_power"`
InMinOut string
BatVoltage string `json:"battery_voltage"`
BatCurrent string `json:"battery_current"`
BatPower string `json:"battery_power"`
BatCharge string `json:"battery_charge"`
InFreq string `json:"input_frequency"`
OutFreq string `json:"output_frequency"`
}
func buildTemplateInput(status *mk2driver.Mk2Info) *templateInput {
outPower := status.OutVoltage * status.OutCurrent
inPower := status.InCurrent * status.InVoltage
newInput := &templateInput{
Date: status.Timestamp.Format(time.RFC1123Z),
OutCurrent: fmt.Sprintf("%.2f", status.OutCurrent),
OutVoltage: fmt.Sprintf("%.2f", status.OutVoltage),
OutPower: fmt.Sprintf("%.2f", outPower),
InCurrent: fmt.Sprintf("%.2f", status.InCurrent),
InVoltage: fmt.Sprintf("%.2f", status.InVoltage),
InFreq: fmt.Sprintf("%.2f", status.InFrequency),
OutFreq: fmt.Sprintf("%.2f", status.OutFrequency),
InPower: fmt.Sprintf("%.2f", inPower),
InMinOut: fmt.Sprintf("%.2f", inPower-outPower),
BatCurrent: fmt.Sprintf("%.2f", status.BatCurrent),
BatVoltage: fmt.Sprintf("%.2f", status.BatVoltage),
BatPower: fmt.Sprintf("%.2f", status.BatVoltage*status.BatCurrent),
BatCharge: fmt.Sprintf("%.2f", status.ChargeState*100),
}
return newInput
}

View File

@@ -0,0 +1,82 @@
package munin
const muninConfig = `multigraph in_batvolt
graph_title Battery Voltage
graph_vlabel Voltage (V)
graph_category inverter
graph_info Battery voltage
volt.info Voltage of battery
volt.label Voltage of battery (V)
multigraph in_batcharge
graph_title Battery Charge
graph_vlabel Charge (%)
graph_category inverter
graph_info Battery charge
charge.info Estimated charge of battery
charge.label Battery charge (%)
multigraph in_batcurrent
graph_title Battery Current
graph_vlabel Current (A)
graph_category inverter
graph_info Battery current
current.info Battery current
current.label Battery current (A)
multigraph in_batpower
graph_title Battery Power
graph_vlabel Power (W)
graph_category inverter
graph_info Battery power
power.info Battery power
power.label Battery power (W)
multigraph in_mainscurrent
graph_title Mains Current
graph_vlabel Current (A)
graph_category inverter
graph_info Mains current
currentin.info Input current
currentin.label Input current (A)
currentout.info Output current
currentout.label Output current (A)
multigraph in_mainsvoltage
graph_title Mains Voltage
graph_vlabel Voltage (V)
graph_category inverter
graph_info Mains voltage
voltagein.info Input voltage
voltagein.label Input voltage (V)
voltageout.info Output voltage
voltageout.label Output voltage (V)
multigraph in_mainspower
graph_title Mains Power
graph_vlabel Power (VA)
graph_category inverter
graph_info Mains power
powerin.info Input power
powerin.label Input power (VA)
powerout.info Output power
powerout.label Output power (VA)
multigraph in_mainsfreq
graph_title Mains frequency
graph_vlabel Frequency (Hz)
graph_category inverter
graph_info Mains frequency
freqin.info In frequency
freqin.label In frequency (Hz)
freqout.info Out frequency
freqout.label Out frequency (Hz)
`

View File

@@ -28,14 +28,15 @@ 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. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package webgui package prometheus
import ( import (
"github.com/diebietse/invertergui/mk2driver" "github.com/diebietse/invertergui/mk2driver"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
type prometheusUpdater struct { type Prometheus struct {
mk2driver.Mk2
batteryVoltage prometheus.Gauge batteryVoltage prometheus.Gauge
batteryCharge prometheus.Gauge batteryCharge prometheus.Gauge
batteryCurrent prometheus.Gauge batteryCurrent prometheus.Gauge
@@ -50,8 +51,9 @@ type prometheusUpdater struct {
mainsFreqOut prometheus.Gauge mainsFreqOut prometheus.Gauge
} }
func newPrometheusUpdater() *prometheusUpdater { func NewPrometheus(mk2 mk2driver.Mk2) {
tmp := &prometheusUpdater{ tmp := &Prometheus{
Mk2: mk2,
batteryVoltage: prometheus.NewGauge(prometheus.GaugeOpts{ batteryVoltage: prometheus.NewGauge(prometheus.GaugeOpts{
Name: "battery_voltage_v", Name: "battery_voltage_v",
Help: "Voltage of the battery.", Help: "Voltage of the battery.",
@@ -101,7 +103,8 @@ func newPrometheusUpdater() *prometheusUpdater {
Help: "Mains frequency at inverter output", Help: "Mains frequency at inverter output",
}), }),
} }
prometheus.MustRegister(tmp.batteryVoltage, prometheus.MustRegister(
tmp.batteryVoltage,
tmp.batteryCharge, tmp.batteryCharge,
tmp.batteryCurrent, tmp.batteryCurrent,
tmp.batteryPower, tmp.batteryPower,
@@ -114,21 +117,30 @@ func newPrometheusUpdater() *prometheusUpdater {
tmp.mainsFreqIn, tmp.mainsFreqIn,
tmp.mainsFreqOut, tmp.mainsFreqOut,
) )
return tmp
go tmp.run()
} }
func (pu *prometheusUpdater) updatePrometheus(newStatus *mk2driver.Mk2Info) { func (p *Prometheus) run() {
s := newStatus for e := range p.C() {
pu.batteryVoltage.Set(s.BatVoltage) if e.Valid {
pu.batteryCharge.Set(newStatus.ChargeState * 100) p.updatePrometheus(e)
pu.batteryCurrent.Set(s.BatCurrent) }
pu.batteryPower.Set(s.BatVoltage * s.BatCurrent) }
pu.mainsCurrentIn.Set(s.InCurrent) }
pu.mainsCurrentOut.Set(s.OutCurrent)
pu.mainsVoltageIn.Set(s.InVoltage) func (p *Prometheus) updatePrometheus(newStatus *mk2driver.Mk2Info) {
pu.mainsVoltageOut.Set(s.OutVoltage) s := newStatus
pu.mainsPowerIn.Set(s.InVoltage * s.InCurrent) p.batteryVoltage.Set(s.BatVoltage)
pu.mainsPowerOut.Set(s.OutVoltage * s.OutCurrent) p.batteryCharge.Set(newStatus.ChargeState * 100)
pu.mainsFreqIn.Set(s.InFrequency) p.batteryCurrent.Set(s.BatCurrent)
pu.mainsFreqOut.Set(s.OutFrequency) p.batteryPower.Set(s.BatVoltage * s.BatCurrent)
p.mainsCurrentIn.Set(s.InCurrent)
p.mainsCurrentOut.Set(s.OutCurrent)
p.mainsVoltageIn.Set(s.InVoltage)
p.mainsVoltageOut.Set(s.OutVoltage)
p.mainsPowerIn.Set(s.InVoltage * s.InCurrent)
p.mainsPowerOut.Set(s.OutVoltage * s.OutCurrent)
p.mainsFreqIn.Set(s.InFrequency)
p.mainsFreqOut.Set(s.OutFrequency)
} }

View File

@@ -28,7 +28,7 @@ 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. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package webgui package webui
import ( import (
"fmt" "fmt"
@@ -50,24 +50,19 @@ const (
) )
type WebGui struct { type WebGui struct {
mk2driver.Mk2
stopChan chan struct{} stopChan chan struct{}
muninRespChan chan muninData
poller mk2driver.Mk2
wg sync.WaitGroup wg sync.WaitGroup
hub *websocket.Hub hub *websocket.Hub
pu *prometheusUpdater
} }
func NewWebGui(source mk2driver.Mk2) *WebGui { func NewWebGui(source mk2driver.Mk2) *WebGui {
w := new(WebGui) w := &WebGui{
w.muninRespChan = make(chan muninData) stopChan: make(chan struct{}),
w.stopChan = make(chan struct{}) Mk2: source,
w.poller = source hub: websocket.NewHub(),
w.pu = newPrometheusUpdater() }
w.hub = websocket.NewHub()
w.wg.Add(1) w.wg.Add(1)
go w.dataPoll() go w.dataPoll()
return w return w
@@ -99,14 +94,6 @@ type templateInput struct {
LedMap map[string]string `json:"led_map"` LedMap map[string]string `json:"led_map"`
} }
func (w *WebGui) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
http.ServeFile(rw, r, "./frontend/index.html")
}
func (w *WebGui) ServeJS(rw http.ResponseWriter, r *http.Request) {
http.ServeFile(rw, r, "./frontend/js/controller.js")
}
func (w *WebGui) ServeHub(rw http.ResponseWriter, r *http.Request) { func (w *WebGui) ServeHub(rw http.ResponseWriter, r *http.Request) {
w.hub.ServeHTTP(rw, r) w.hub.ServeHTTP(rw, r)
} }
@@ -176,21 +163,15 @@ func (w *WebGui) Stop() {
// 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() { func (w *WebGui) dataPoll() {
pollChan := w.poller.C()
var muninValues muninData
for { for {
select { select {
case s := <-pollChan: case s := <-w.C():
if s.Valid { if s.Valid {
calcMuninValues(&muninValues, s)
w.pu.updatePrometheus(s)
err := w.hub.Broadcast(buildTemplateInput(s)) err := w.hub.Broadcast(buildTemplateInput(s))
if err != nil { if err != nil {
log.Printf("Could not send update to clients: %v", err) log.Printf("Could not send update to clients: %v", err)
} }
} }
case w.muninRespChan <- muninValues:
zeroMuninValues(&muninValues)
case <-w.stopChan: case <-w.stopChan:
w.wg.Done() w.wg.Done()
return return

View File

@@ -28,7 +28,7 @@ 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. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package webgui package webui
import ( import (
"fmt" "fmt"