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
|
||||
#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
|
||||
|
||||
guimock:
|
||||
go build ./cmd/guimock/
|
||||
.DEFAULT_GOAL = invertergui
|
||||
|
||||
invertergui:
|
||||
go build ./cmd/invertergui/
|
||||
|
||||
invertercli:
|
||||
go build ./cmd/invertercli/
|
||||
|
||||
build: guimock invertergui invertercli
|
||||
|
||||
all: build gofmt test
|
||||
|
||||
gofmt:
|
||||
@@ -62,4 +54,4 @@ lint:
|
||||
golangci-lint run
|
||||
|
||||
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"
|
||||
|
||||
"github.com/diebietse/invertergui/frontend"
|
||||
"github.com/diebietse/invertergui/mk2core"
|
||||
"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/tarm/serial"
|
||||
)
|
||||
@@ -50,14 +54,44 @@ func main() {
|
||||
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.")
|
||||
mock := flag.Bool("mock", false, "Creates a mock device for test puposes")
|
||||
cliEnable := flag.Bool("cli", false, "Enable CLI output")
|
||||
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 err error
|
||||
var tcpAddr *net.TCPAddr
|
||||
|
||||
if *tcp {
|
||||
tcpAddr, err = net.ResolveTCPAddr("tcp", *ip)
|
||||
if tcp {
|
||||
tcpAddr, err = net.ResolveTCPAddr("tcp", ip)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -66,25 +100,16 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
serialConfig := &serial.Config{Name: *dev, Baud: 2400}
|
||||
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()
|
||||
|
||||
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))
|
||||
return mk2
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -64,7 +63,6 @@ func (m *mock) genMockValues() {
|
||||
if mult < 0 {
|
||||
mult = 1.0
|
||||
}
|
||||
fmt.Printf("Sending\n")
|
||||
m.c <- input
|
||||
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.
|
||||
*/
|
||||
|
||||
package webgui
|
||||
package munin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
)
|
||||
|
||||
type Munin struct {
|
||||
mk2driver.Mk2
|
||||
muninResponse chan *muninData
|
||||
}
|
||||
|
||||
type muninData struct {
|
||||
status mk2driver.Mk2Info
|
||||
status *mk2driver.Mk2Info
|
||||
timesUpdated int
|
||||
}
|
||||
|
||||
func (w *WebGui) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
muninDat := <-w.muninRespChan
|
||||
func NewMunin(mk2 mk2driver.Mk2) *Munin {
|
||||
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 {
|
||||
rw.WriteHeader(500)
|
||||
_, _ = rw.Write([]byte("No data to return.\n"))
|
||||
return
|
||||
}
|
||||
calcMuninAverages(&muninDat)
|
||||
calcMuninAverages(muninDat)
|
||||
|
||||
status := muninDat.status
|
||||
tmpInput := buildTemplateInput(&status)
|
||||
tmpInput := buildTemplateInput(status)
|
||||
outputBuf := &bytes.Buffer{}
|
||||
fmt.Fprintf(outputBuf, "multigraph in_batvolt\n")
|
||||
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) {
|
||||
output := `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)
|
||||
`
|
||||
|
||||
func (m *Munin) ServeMuninConfigHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
output := muninConfig
|
||||
_, err := rw.Write([]byte(output))
|
||||
if err != nil {
|
||||
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.
|
||||
func calcMuninValues(muninDat *muninData, newStatus *mk2driver.Mk2Info) {
|
||||
muninDat.timesUpdated++
|
||||
muninVal := &muninDat.status
|
||||
muninVal := muninDat.status
|
||||
muninVal.OutCurrent += newStatus.OutCurrent
|
||||
muninVal.InCurrent += newStatus.InCurrent
|
||||
muninVal.BatCurrent += newStatus.BatCurrent
|
||||
@@ -189,7 +143,7 @@ func calcMuninValues(muninDat *muninData, newStatus *mk2driver.Mk2Info) {
|
||||
}
|
||||
|
||||
func calcMuninAverages(muninDat *muninData) {
|
||||
muninVal := &muninDat.status
|
||||
muninVal := muninDat.status
|
||||
muninVal.OutCurrent /= float64(muninDat.timesUpdated)
|
||||
muninVal.InCurrent /= float64(muninDat.timesUpdated)
|
||||
muninVal.BatCurrent /= float64(muninDat.timesUpdated)
|
||||
@@ -201,7 +155,7 @@ func calcMuninAverages(muninDat *muninData) {
|
||||
|
||||
func zeroMuninValues(muninDat *muninData) {
|
||||
muninDat.timesUpdated = 0
|
||||
muninVal := &muninDat.status
|
||||
muninVal := muninDat.status
|
||||
muninVal.OutCurrent = 0
|
||||
muninVal.InCurrent = 0
|
||||
muninVal.BatCurrent = 0
|
||||
@@ -215,3 +169,50 @@ func zeroMuninValues(muninDat *muninData) {
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package webgui
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type prometheusUpdater struct {
|
||||
type Prometheus struct {
|
||||
mk2driver.Mk2
|
||||
batteryVoltage prometheus.Gauge
|
||||
batteryCharge prometheus.Gauge
|
||||
batteryCurrent prometheus.Gauge
|
||||
@@ -50,8 +51,9 @@ type prometheusUpdater struct {
|
||||
mainsFreqOut prometheus.Gauge
|
||||
}
|
||||
|
||||
func newPrometheusUpdater() *prometheusUpdater {
|
||||
tmp := &prometheusUpdater{
|
||||
func NewPrometheus(mk2 mk2driver.Mk2) {
|
||||
tmp := &Prometheus{
|
||||
Mk2: mk2,
|
||||
batteryVoltage: prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "battery_voltage_v",
|
||||
Help: "Voltage of the battery.",
|
||||
@@ -101,7 +103,8 @@ func newPrometheusUpdater() *prometheusUpdater {
|
||||
Help: "Mains frequency at inverter output",
|
||||
}),
|
||||
}
|
||||
prometheus.MustRegister(tmp.batteryVoltage,
|
||||
prometheus.MustRegister(
|
||||
tmp.batteryVoltage,
|
||||
tmp.batteryCharge,
|
||||
tmp.batteryCurrent,
|
||||
tmp.batteryPower,
|
||||
@@ -114,21 +117,30 @@ func newPrometheusUpdater() *prometheusUpdater {
|
||||
tmp.mainsFreqIn,
|
||||
tmp.mainsFreqOut,
|
||||
)
|
||||
return tmp
|
||||
|
||||
go tmp.run()
|
||||
}
|
||||
|
||||
func (pu *prometheusUpdater) updatePrometheus(newStatus *mk2driver.Mk2Info) {
|
||||
s := newStatus
|
||||
pu.batteryVoltage.Set(s.BatVoltage)
|
||||
pu.batteryCharge.Set(newStatus.ChargeState * 100)
|
||||
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)
|
||||
pu.mainsVoltageOut.Set(s.OutVoltage)
|
||||
pu.mainsPowerIn.Set(s.InVoltage * s.InCurrent)
|
||||
pu.mainsPowerOut.Set(s.OutVoltage * s.OutCurrent)
|
||||
pu.mainsFreqIn.Set(s.InFrequency)
|
||||
pu.mainsFreqOut.Set(s.OutFrequency)
|
||||
func (p *Prometheus) run() {
|
||||
for e := range p.C() {
|
||||
if e.Valid {
|
||||
p.updatePrometheus(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prometheus) updatePrometheus(newStatus *mk2driver.Mk2Info) {
|
||||
s := newStatus
|
||||
p.batteryVoltage.Set(s.BatVoltage)
|
||||
p.batteryCharge.Set(newStatus.ChargeState * 100)
|
||||
p.batteryCurrent.Set(s.BatCurrent)
|
||||
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.
|
||||
*/
|
||||
|
||||
package webgui
|
||||
package webui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -50,24 +50,19 @@ const (
|
||||
)
|
||||
|
||||
type WebGui struct {
|
||||
mk2driver.Mk2
|
||||
stopChan chan struct{}
|
||||
|
||||
muninRespChan chan muninData
|
||||
poller mk2driver.Mk2
|
||||
wg sync.WaitGroup
|
||||
hub *websocket.Hub
|
||||
|
||||
pu *prometheusUpdater
|
||||
wg sync.WaitGroup
|
||||
hub *websocket.Hub
|
||||
}
|
||||
|
||||
func NewWebGui(source mk2driver.Mk2) *WebGui {
|
||||
w := new(WebGui)
|
||||
w.muninRespChan = make(chan muninData)
|
||||
w.stopChan = make(chan struct{})
|
||||
w.poller = source
|
||||
w.pu = newPrometheusUpdater()
|
||||
w.hub = websocket.NewHub()
|
||||
|
||||
w := &WebGui{
|
||||
stopChan: make(chan struct{}),
|
||||
Mk2: source,
|
||||
hub: websocket.NewHub(),
|
||||
}
|
||||
w.wg.Add(1)
|
||||
go w.dataPoll()
|
||||
return w
|
||||
@@ -99,14 +94,6 @@ type templateInput struct {
|
||||
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) {
|
||||
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
|
||||
// to respChan if anything reads from it.
|
||||
func (w *WebGui) dataPoll() {
|
||||
pollChan := w.poller.C()
|
||||
var muninValues muninData
|
||||
for {
|
||||
select {
|
||||
case s := <-pollChan:
|
||||
case s := <-w.C():
|
||||
if s.Valid {
|
||||
calcMuninValues(&muninValues, s)
|
||||
w.pu.updatePrometheus(s)
|
||||
err := w.hub.Broadcast(buildTemplateInput(s))
|
||||
if err != nil {
|
||||
log.Printf("Could not send update to clients: %v", err)
|
||||
}
|
||||
}
|
||||
case w.muninRespChan <- muninValues:
|
||||
zeroMuninValues(&muninValues)
|
||||
case <-w.stopChan:
|
||||
w.wg.Done()
|
||||
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.
|
||||
*/
|
||||
|
||||
package webgui
|
||||
package webui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
Reference in New Issue
Block a user