Make input registers GaugeFuncs

This commit is contained in:
Josh Deprez 2023-01-07 17:37:54 +11:00
parent 5fd288681b
commit ea6f44e867
No known key found for this signature in database

51
main.go
View file

@ -25,6 +25,7 @@ import (
"log" "log"
"net/http" "net/http"
"strings" "strings"
"sync"
"time" "time"
"gitea.drjosh.dev/josh/sungrow/modbus" "gitea.drjosh.dev/josh/sungrow/modbus"
@ -33,12 +34,16 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
) )
const maxScrapeAge = 3 * time.Second
var ( var (
httpAddr = flag.String("http-addr", ":9455", "Address to listen on") httpAddr = flag.String("http-addr", ":9455", "Address to listen on")
inverterAddrs = flag.String("inverter-addrs", "rakmodule_00DBC1:502,192.168.86.6:502", "Comma-separated list of inverter addresses (modbus-tcp with 'encryption')") inverterAddrs = flag.String("inverter-addrs", "rakmodule_00DBC1:502,192.168.86.6:502", "Comma-separated list of inverter addresses (modbus-tcp with 'encryption')")
scrapeInterval = flag.Duration("scrape-interval", 15*time.Second, "Period of modbus scraping loop") scrapeInterval = flag.Duration("scrape-interval", 15*time.Second, "Period of modbus scraping loop")
registerGauges = make(map[uint16]prometheus.Gauge) scrapeMu sync.Mutex
lastScrape time.Time
lastValues = make(map[uint16]float64)
scrapeCounter = promauto.NewCounter(prometheus.CounterOpts{ scrapeCounter = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "sungrow", Namespace: "sungrow",
@ -100,21 +105,13 @@ func init() {
}, },
func() float64 { return solarFeedInTariff.pricePerKWh(time.Now()) }, func() float64 { return solarFeedInTariff.pricePerKWh(time.Now()) },
) )
for addr, reg := range sungrowInputRegs {
registerGauges[addr] = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: "sungrow",
Subsystem: "inverter",
Name: reg.name,
Help: fmt.Sprintf("addr: %d, unit: %s", addr, reg.unit),
})
}
} }
func statusHandler(w http.ResponseWriter, r *http.Request) { func statusHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "current time: %v\n", time.Now()) fmt.Fprintf(w, "current time: %v\n", time.Now())
} }
// Called under scrapeMu.
func readRegs(client modbus.Client, start, qty uint16) { func readRegs(client modbus.Client, start, qty uint16) {
data, err := client.ReadInputRegisters(start, qty) data, err := client.ReadInputRegisters(start, qty)
if err != nil { if err != nil {
@ -136,16 +133,18 @@ func readRegs(client modbus.Client, start, qty uint16) {
log.Fatalf("Couldn't parse input register data at %d: %v", addr, err) log.Fatalf("Couldn't parse input register data at %d: %v", addr, err)
} }
//fmt.Printf("%s: %v %s\n", reg.name, val, reg.unit) //fmt.Printf("%s: %v %s\n", reg.name, val, reg.unit)
registerGauges[addr].Set(val) lastValues[addr] = val
} }
} }
// Called under scrapeMu.
func scrape(client modbus.Client) { func scrape(client modbus.Client) {
start := time.Now() start := time.Now()
scrapeStart.SetToCurrentTime() scrapeStart.SetToCurrentTime()
readRegs(client, 5000, 100) readRegs(client, 5000, 100)
readRegs(client, 5100, 50) readRegs(client, 5100, 50)
scrapeEnd.SetToCurrentTime() scrapeEnd.SetToCurrentTime()
lastScrape = time.Now()
scrapeDuration.Set(time.Since(start).Seconds()) scrapeDuration.Set(time.Since(start).Seconds())
scrapeCounter.Inc() scrapeCounter.Inc()
} }
@ -173,18 +172,34 @@ func main() {
http.Handle("/metrics", promhttp.Handler()) http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", statusHandler) http.HandleFunc("/", statusHandler)
// Modbus scrape loop
handler := modbus.TCPHandlerFromConnection(sgc) handler := modbus.TCPHandlerFromConnection(sgc)
handler.SlaveId = 0x01 handler.SlaveId = 0x01
//handler.Connect() //handler.Connect()
defer handler.Close() defer handler.Close()
client := modbus.NewClient(handler) client := modbus.NewClient(handler)
// Finally, create all the register gauges.
// These are GaugeFuncs to more closely align Prometheus scrape time with
// the modbus scrape time.
for addr, reg := range sungrowInputRegs {
promauto.NewGaugeFunc(
prometheus.GaugeOpts{
Namespace: "sungrow",
Subsystem: "inverter",
Name: reg.name,
Help: fmt.Sprintf("addr: %d, unit: %s", addr, reg.unit),
},
func() float64 {
scrapeMu.Lock()
defer scrapeMu.Unlock()
if time.Since(lastScrape) <= maxScrapeAge {
return lastValues[addr]
}
scrape(client) scrape(client)
// Start http interface only after first successful scrape return lastValues[addr]
go func() { },
)
}
log.Fatalf("http.ListenAndServe: %v", http.ListenAndServe(*httpAddr, nil)) log.Fatalf("http.ListenAndServe: %v", http.ListenAndServe(*httpAddr, nil))
}()
for range time.Tick(*scrapeInterval) {
scrape(client)
}
} }