wo-bisch-web/metrics.go

362 lines
10 KiB
Go

package main
import (
"bufio"
"bytes"
"fmt"
"html/template"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
)
type OneMetric struct {
Deveui string
DeveuiJS template.JS
Alias string
Readonly bool
Timestamp string
Lat string
Lon string
LatJS template.JS
LonJS template.JS
Vbat string
Fw string
BatteryPercent string
ActiveUntil string
DaysUntilDeactivated int // berechneter Wert
}
// metrics handler
func validProperty(prop string) bool {
valid_properties := [...]string{"lat", "lon", "vbat", "fw"}
for _, p := range valid_properties {
if p == prop {
return true
}
}
return false
}
func metricsHandler(response http.ResponseWriter, request *http.Request) {
name := getUserName(request)
if name != "" {
property, ok := request.URL.Query()["property"]
if !ok || len(property[0]) < 1 {
log.Println("Url Param 'property' is missing")
fmt.Fprintf(response, "{ \"msg\": \"error: property must be specified in URL\" }")
return
}
if !validProperty(property[0]) {
log.Println("Url Param 'property' is invalid")
fmt.Fprintf(response, "{ \"msg\": \"error: invalid property\" }")
return
}
deveui, ok := request.URL.Query()["deveui"]
if !ok || len(deveui[0]) < 1 {
log.Println("Url Param 'deveui' is missing")
fmt.Fprintf(response, "{ \"msg\": \"error: deveui must be specified in URL\" }")
return
}
// Query()["deveui"] will return an array of items,
// we only want the single item.
mydeveui := deveui[0]
if !(Contains(getMyDevs(name), mydeveui)) {
log.Println("specified 'deveui' does not belong to this user")
fmt.Fprintf(response, "{ \"msg\": \"error: specified deveui does not belong to this user\" }")
return
}
if AboExpired(mydeveui) {
log.Println("specified 'deveui' has an expired abo")
fmt.Fprintf(response, "{ \"msg\": \"specified deveui has an expired abo\" }")
return
}
log.Println("Url Param 'deveui' is: " + string(mydeveui))
// Format of start and stop: YYYY-MM-DDTHH:MI:SSZ
stop, ok := request.URL.Query()["stop"]
var mystop string
if !ok || len(stop[0]) < 1 {
log.Println("Url Param 'stop' is missing, set it to now")
mystop = time.Now().Format("2006-01-02T15:04:05Z")
}
if ok {
mystop = stop[0]
}
layout := "2006-01-02T15:04:05Z"
stopDate, err := time.Parse(layout, mystop)
if err != nil {
fmt.Println(err)
}
start, ok := request.URL.Query()["start"]
var mystart string
if !ok || len(start[0]) < 1 {
log.Println("Url Param 'start' is missing, set it to stop minus one day")
t := stopDate.AddDate(0, 0, -1)
mystart = t.Format("2006-01-02T15:04:05Z")
}
if ok {
mystart = start[0]
}
url := "http://localhost:8086/api/v2/query?org=wobischorg"
data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket") |> range(start: %s, stop: %s) |> filter(fn: (r) => r._measurement == "measurement") |> filter(fn: (r) => r._field == "%s") |> filter(fn: (r) => r.deveui == "%s")`, mystart, mystop, property[0], mydeveui))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
log.Fatal("Error reading request. ", err)
}
// Set headers
req.Header.Set("Authorization", "Token TQvQxxLLAj1kTKWuEqcx7BA-KfE6WtJUeDlPa_Dnvms6Zqf6uh6lMbpXtzcsCjKO_x3PrpxxGDR5E6YnDB5PFg==")
req.Header.Set("accept", "application/csv")
req.Header.Set("content-type", "application/vnd.flux")
// Set client timeout
client := &http.Client{Timeout: time.Second * 10}
// Send request
resp, err := client.Do(req)
if err != nil {
log.Fatal("Error reading response. ", err)
}
defer resp.Body.Close()
fmt.Println("response Status:", resp.Status)
fmt.Println("response Headers:", resp.Header)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal("Error reading body. ", err)
}
fmt.Fprintf(response, "[\n")
scanner := bufio.NewScanner(strings.NewReader(string(body)))
first := true
for scanner.Scan() {
s := strings.Split(scanner.Text(), ",")
if (len(s) >= 7) && !(strings.HasPrefix(s[5], "_")) {
t, err := time.Parse(time.RFC3339, s[5])
if err != nil {
continue
}
a := t.Unix()
b := s[6]
if !(first) {
fmt.Fprintf(response, ",")
} else {
first = false
}
fmt.Fprintf(response, "[%d000,%s]\n", a, b)
}
}
fmt.Fprintf(response, "]\n")
} else {
fmt.Fprintf(response, "{ \"msg\": \"Only available for logged in users\" }")
}
}
func lastmetricsHandler(response http.ResponseWriter, request *http.Request) {
name := getUserName(request)
if name != "" {
deveui, ok := request.URL.Query()["deveui"]
if !ok || len(deveui[0]) < 1 {
log.Println("Url Param 'deveui' is missing")
fmt.Fprintf(response, "{ \"msg\": \"deveui must be specified in URL\" }")
return
}
// Query()["deveui"] will return an array of items,
// we only want the single item.
mydeveui := deveui[0]
if !(Contains(getMyDevs(name), mydeveui)) {
log.Println("specified 'deveui' does not belong to this user")
fmt.Fprintf(response, "{ \"msg\": \"specified deveui does not belong to this user\" }")
return
}
log.Println("Url Param 'deveui' is: " + string(mydeveui))
url := "http://localhost:8086/api/v2/query?org=wobischorg"
data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket")
|> range(start:-365d)
|> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
|> filter(fn: (r) => r._field == "lat" or r._field == "lon" or r._field == "vbat" or r._field == "fw")
|> last(column: "_time") |> yield(name: "last")`, mydeveui))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
log.Fatal("Error reading request. ", err)
}
// Set headers
req.Header.Set("Authorization", "Token TQvQxxLLAj1kTKWuEqcx7BA-KfE6WtJUeDlPa_Dnvms6Zqf6uh6lMbpXtzcsCjKO_x3PrpxxGDR5E6YnDB5PFg==")
req.Header.Set("accept", "application/csv")
req.Header.Set("content-type", "application/vnd.flux")
// Set client timeout
client := &http.Client{Timeout: time.Second * 10}
// Send request
resp, err := client.Do(req)
if err != nil {
log.Fatal("Error reading response. ", err)
}
defer resp.Body.Close()
fmt.Println("response Status:", resp.Status)
fmt.Println("response Headers:", resp.Header)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal("Error reading body. ", err)
}
fmt.Println("response Body 1:", string(body))
scanner := bufio.NewScanner(strings.NewReader(string(body)))
ts := ""
lat := ""
lon := ""
vbat := ""
fw := ""
location, err := time.LoadLocation("Europe/Zurich")
for scanner.Scan() {
s := strings.Split(scanner.Text(), ",")
if (len(s) >= 7) && !(strings.HasPrefix(s[5], "_")) {
mytime, err := time.Parse(time.RFC3339, s[5])
if err != nil {
continue
}
ts = mytime.In(location).Format("02.01.2006 15:04")
value := s[6]
field := s[7]
if field == "lat" {
lat = value
} else if field == "lon" {
lon = value
} else if field == "vbat" {
vbat = value
} else if field == "fw" {
fw = value
}
}
}
fmt.Fprintf(response, `{
"ts": "%s",
"lat": "%s",
"lon": "%s",
"vbat": "%s",
"fw": "%s"
}`, ts, lat, lon, vbat, fw)
} else {
fmt.Fprintf(response, "{ \"msg\": \"Only available for logged in users\" }")
}
}
func CalcDaysUntil(mydate string) int {
var days int
layout := "02.01.2006"
t, err := time.Parse(layout, mydate)
if err != nil {
days = 0
}
days = int(t.Sub(time.Now()).Hours() / 24)
return days
}
func getLastMetrics(deveui string) OneMetric {
var res OneMetric
url := "http://localhost:8086/api/v2/query?org=wobischorg"
data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket")
|> range(start:-365d)
|> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
|> filter(fn: (r) => r._field == "lat" or r._field == "lon" or r._field == "vbat" or r._field == "fw")
|> last(column: "_time") |> yield(name: "last")`, deveui))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
log.Fatal("Error reading request. ", err)
}
// Set headers
req.Header.Set("Authorization", "Token TQvQxxLLAj1kTKWuEqcx7BA-KfE6WtJUeDlPa_Dnvms6Zqf6uh6lMbpXtzcsCjKO_x3PrpxxGDR5E6YnDB5PFg==")
req.Header.Set("accept", "application/csv")
req.Header.Set("content-type", "application/vnd.flux")
// Set client timeout
client := &http.Client{Timeout: time.Second * 10}
// Send request
resp, err := client.Do(req)
if err != nil {
log.Fatal("Error reading response. ", err)
}
defer resp.Body.Close()
fmt.Println("response Status:", resp.Status)
fmt.Println("response Headers:", resp.Header)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal("Error reading body. ", err)
}
fmt.Println("response Body 2:", string(body))
scanner := bufio.NewScanner(strings.NewReader(string(body)))
location, err := time.LoadLocation("Europe/Zurich")
for scanner.Scan() {
s := strings.Split(scanner.Text(), ",")
fmt.Printf("BlaBla: %v\n", s)
if (len(s) >= 7) && !(strings.HasPrefix(s[5], "_")) {
mytime, err := time.Parse(time.RFC3339, s[5])
if err != nil {
continue
}
res.Timestamp = mytime.In(location).Format("02.01.2006 15:04")
value := s[6]
field := s[7]
if field == "lat" {
res.Lat = value
res.LatJS = template.JS(value)
} else if field == "lon" {
res.Lon = value
res.LonJS = template.JS(value)
} else if field == "vbat" {
res.Vbat = value
} else if field == "fw" {
res.Fw = value
}
}
}
res.Deveui = deveui
res.DeveuiJS = template.JS(deveui)
res.Alias = getDevAlias(deveui)
res.Readonly = false
res.ActiveUntil = getActiveUntil(deveui)
res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil)
return res
}