Concept plugin layout
This commit is contained in:
committed by
ncthompson
parent
d02de285d9
commit
47e73a4eff
14
Makefile
14
Makefile
@@ -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
|
||||||
@@ -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))
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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
57
mk2core/core.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -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
47
plugins/cli/cli.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
82
plugins/munin/muninconfig.go
Normal file
82
plugins/munin/muninconfig.go
Normal 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)
|
||||||
|
`
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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"
|
||||||
Reference in New Issue
Block a user