Completely rearrange main.go
This commit is contained in:
parent
d198f61ea9
commit
40b56ee7ec
1 changed files with 85 additions and 50 deletions
135
main.go
135
main.go
|
@ -41,24 +41,28 @@ var (
|
||||||
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")
|
||||||
|
|
||||||
scrapeMu sync.Mutex
|
promHandler = promhttp.Handler()
|
||||||
lastScrape time.Time
|
scrapeMu sync.Mutex
|
||||||
lastValues = make(map[uint16]float64)
|
lastScrape time.Time
|
||||||
|
lastValues = make(map[uint16]float64)
|
||||||
|
|
||||||
scrapeCounter = promauto.NewCounter(prometheus.CounterOpts{
|
scrapeCounter = promauto.NewCounter(prometheus.CounterOpts{
|
||||||
Namespace: "sungrow",
|
Namespace: "sungrow",
|
||||||
Subsystem: "scraper",
|
Subsystem: "scraper",
|
||||||
Name: "scrapes_total",
|
Name: "scrapes_total",
|
||||||
|
Help: "Number of successful scrapes of the inverter input registers",
|
||||||
})
|
})
|
||||||
scrapeStart = promauto.NewGauge(prometheus.GaugeOpts{
|
scrapeStart = promauto.NewGauge(prometheus.GaugeOpts{
|
||||||
Namespace: "sungrow",
|
Namespace: "sungrow",
|
||||||
Subsystem: "scraper",
|
Subsystem: "scraper",
|
||||||
Name: "scrape_start",
|
Name: "scrape_start",
|
||||||
|
Help: "Start time of the most recent scrape attempt",
|
||||||
})
|
})
|
||||||
scrapeEnd = promauto.NewGauge(prometheus.GaugeOpts{
|
scrapeEnd = promauto.NewGauge(prometheus.GaugeOpts{
|
||||||
Namespace: "sungrow",
|
Namespace: "sungrow",
|
||||||
Subsystem: "scraper",
|
Subsystem: "scraper",
|
||||||
Name: "scrape_end",
|
Name: "scrape_end",
|
||||||
|
Help: "End time of the most recent successful scrape",
|
||||||
})
|
})
|
||||||
scrapeDuration = promauto.NewGauge(prometheus.GaugeOpts{
|
scrapeDuration = promauto.NewGauge(prometheus.GaugeOpts{
|
||||||
Namespace: "sungrow",
|
Namespace: "sungrow",
|
||||||
|
@ -83,7 +87,7 @@ func init() {
|
||||||
Namespace: "sungrow",
|
Namespace: "sungrow",
|
||||||
Subsystem: "tariff",
|
Subsystem: "tariff",
|
||||||
Name: "daily_charge",
|
Name: "daily_charge",
|
||||||
Help: "unit:$",
|
Help: "units:$",
|
||||||
},
|
},
|
||||||
func() float64 { return dailySupplyCharge },
|
func() float64 { return dailySupplyCharge },
|
||||||
)
|
)
|
||||||
|
@ -92,7 +96,7 @@ func init() {
|
||||||
Namespace: "sungrow",
|
Namespace: "sungrow",
|
||||||
Subsystem: "tariff",
|
Subsystem: "tariff",
|
||||||
Name: "import_tariff",
|
Name: "import_tariff",
|
||||||
Help: "unit:$",
|
Help: "units:$",
|
||||||
},
|
},
|
||||||
func() float64 { return tariff93.pricePerKWh(time.Now()) },
|
func() float64 { return tariff93.pricePerKWh(time.Now()) },
|
||||||
)
|
)
|
||||||
|
@ -101,7 +105,7 @@ func init() {
|
||||||
Namespace: "sungrow",
|
Namespace: "sungrow",
|
||||||
Subsystem: "tariff",
|
Subsystem: "tariff",
|
||||||
Name: "export_tariff",
|
Name: "export_tariff",
|
||||||
Help: "unit:$",
|
Help: "units:$",
|
||||||
},
|
},
|
||||||
func() float64 { return solarFeedInTariff.pricePerKWh(time.Now()) },
|
func() float64 { return solarFeedInTariff.pricePerKWh(time.Now()) },
|
||||||
)
|
)
|
||||||
|
@ -111,14 +115,26 @@ 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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dialInverter() (*sungrowConn, error) {
|
||||||
|
for _, addr := range strings.Split(*inverterAddrs, ",") {
|
||||||
|
conn, err := dialSungrow(addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Couldn't dial inverter: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("all addresses unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
// Called under scrapeMu.
|
// Called under scrapeMu.
|
||||||
func readRegs(client modbus.Client, start, qty uint16) {
|
func readRegs(client modbus.Client, start, qty uint16) error {
|
||||||
data, err := client.ReadInputRegisters(start, qty)
|
data, err := client.ReadInputRegisters(start, qty)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Couldn't read input registers %d-%d: %v", start+1, start+qty, err)
|
return fmt.Errorf("read input registers %d-%d: %v", start+1, start+qty, err)
|
||||||
}
|
}
|
||||||
if len(data) != int(2*qty) {
|
if len(data) != int(2*qty) {
|
||||||
log.Fatalf("Couldn't read input registers %d-%d: len(data) = %d != %d = 2*qty", start+1, start+qty, len(data), 2*qty)
|
return fmt.Errorf("reading input registers %d-%d: len(data) = %d != %d = 2*qty", start+1, start+qty, len(data), 2*qty)
|
||||||
}
|
}
|
||||||
for addr, reg := range sungrowInputRegs {
|
for addr, reg := range sungrowInputRegs {
|
||||||
if addr <= start || addr > start+qty {
|
if addr <= start || addr > start+qty {
|
||||||
|
@ -130,56 +146,72 @@ func readRegs(client modbus.Client, start, qty uint16) {
|
||||||
log.Printf("Couldn't parse input register data at %d, skipping: %v", addr, err)
|
log.Printf("Couldn't parse input register data at %d, skipping: %v", addr, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Fatalf("Couldn't parse input register data at %d: %v", addr, err)
|
return fmt.Errorf("parsing 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)
|
||||||
lastValues[addr] = val
|
lastValues[addr] = val
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called under scrapeMu.
|
// Called under scrapeMu.
|
||||||
func scrape(client modbus.Client) {
|
func scrape() error {
|
||||||
|
sgc, err := dialInverter()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sgc.Close()
|
||||||
|
|
||||||
|
handler := modbus.TCPHandlerFromConnection(sgc)
|
||||||
|
handler.SlaveId = 0x01
|
||||||
|
//handler.Connect()
|
||||||
|
|
||||||
|
client := modbus.NewClient(handler)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
scrapeStart.SetToCurrentTime()
|
scrapeStart.SetToCurrentTime()
|
||||||
readRegs(client, 5000, 50)
|
if err := readRegs(client, 5000, 50); err != nil {
|
||||||
readRegs(client, 5050, 50)
|
return err
|
||||||
readRegs(client, 5100, 50)
|
}
|
||||||
|
if err := readRegs(client, 5050, 50); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := readRegs(client, 5100, 50); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
scrapeEnd.SetToCurrentTime()
|
scrapeEnd.SetToCurrentTime()
|
||||||
lastScrape = time.Now()
|
lastScrape = time.Now()
|
||||||
scrapeDuration.Set(time.Since(start).Seconds())
|
scrapeDuration.Set(time.Since(start).Seconds())
|
||||||
scrapeCounter.Inc()
|
scrapeCounter.Inc()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func metricsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// In normal mode, always serve metrics
|
||||||
|
defer promHandler.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
scrapeMu.Lock()
|
||||||
|
defer scrapeMu.Unlock()
|
||||||
|
if time.Since(lastScrape) <= maxScrapeAge {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastErr error
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
if err := scrape(); err != nil {
|
||||||
|
log.Printf("Retrying scrape; error: %v", err)
|
||||||
|
lastErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Fatalf("Scrape failed, bailing entirely: %v", lastErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// Is the inverter reachable?
|
|
||||||
var sgc *sungrowConn
|
|
||||||
for _, addr := range strings.Split(*inverterAddrs, ",") {
|
|
||||||
conn, err := dialSungrow(addr)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Couldn't dial inverter: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sgc = conn
|
|
||||||
defer conn.Close()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if sgc == nil {
|
|
||||||
log.Fatal("Couldn't dial any addresses, aborting")
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP setup
|
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
|
||||||
http.HandleFunc("/", statusHandler)
|
|
||||||
|
|
||||||
handler := modbus.TCPHandlerFromConnection(sgc)
|
|
||||||
handler.SlaveId = 0x01
|
|
||||||
//handler.Connect()
|
|
||||||
defer handler.Close()
|
|
||||||
client := modbus.NewClient(handler)
|
|
||||||
|
|
||||||
// Finally, create all the register gauges.
|
|
||||||
// These are GaugeFuncs to more closely align Prometheus scrape time with
|
// These are GaugeFuncs to more closely align Prometheus scrape time with
|
||||||
// the modbus scrape time.
|
// the modbus scrape time.
|
||||||
for addr, reg := range sungrowInputRegs {
|
for addr, reg := range sungrowInputRegs {
|
||||||
|
@ -191,17 +223,20 @@ func main() {
|
||||||
Name: reg.name,
|
Name: reg.name,
|
||||||
Help: fmt.Sprintf("addr: %d, unit: %s", addr, reg.unit),
|
Help: fmt.Sprintf("addr: %d, unit: %s", addr, reg.unit),
|
||||||
},
|
},
|
||||||
func() float64 {
|
func() float64 { return lastValues[addr] },
|
||||||
scrapeMu.Lock()
|
|
||||||
defer scrapeMu.Unlock()
|
|
||||||
if time.Since(lastScrape) <= maxScrapeAge {
|
|
||||||
return lastValues[addr]
|
|
||||||
}
|
|
||||||
scrape(client)
|
|
||||||
return lastValues[addr]
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Startup paranoia check: Is the inverter reachable?
|
||||||
|
sgc, err := dialInverter()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Couldn't dial any addresses, aborting")
|
||||||
|
}
|
||||||
|
sgc.Close()
|
||||||
|
|
||||||
|
// HTTP setup
|
||||||
|
http.HandleFunc("/metrics", metricsHandler)
|
||||||
|
http.HandleFunc("/", statusHandler)
|
||||||
|
|
||||||
log.Fatalf("http.ListenAndServe: %v", http.ListenAndServe(*httpAddr, nil))
|
log.Fatalf("http.ListenAndServe: %v", http.ListenAndServe(*httpAddr, nil))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue