package main import ( "bufio" "bytes" "fmt" "html/template" "io/ioutil" "log" "net/http" "strconv" "strings" "time" ) type OneMetric struct { Deveui string DeveuiJS template.JS Alias string Readonly bool Timestamp string PosTimestamp 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 string2float64(s string) float64 { f, err := strconv.ParseFloat(s, 64) if err == nil { return f } return 0 } func metricsHandler(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\": \"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 == "lon" or r._field == "lat" or r._field == "vbat") |> filter(fn: (r) => r.deveui == "%s") |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")`, mystart, mystop, 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:", string(body)) scanner := bufio.NewScanner(strings.NewReader(string(body))) first := true var lat float64 var lon float64 var min_lat float64 = 9999999999.9 var min_lon float64 = 9999999999.9 var max_lat float64 = 0 var max_lon float64 = 0 fmt.Fprintf(response, "{\n") fmt.Fprintf(response, " \"data\": [\n") for scanner.Scan() { s := strings.Split(scanner.Text(), ",") fmt.Printf("Scanned Line: %v\n", s) if (len(s) >= 11) && !(strings.HasPrefix(s[3], "_")) { t, err := time.Parse(time.RFC3339, s[3]) if err != nil { fmt.Printf("error converting time: %s\n", s[3]) continue } a := t.Unix() lat = string2float64(s[8]) lon = string2float64(s[9]) if (lat == 0) || (lon == 0) { fmt.Println("skip 0 value for lon/lat") } else { if lat < min_lat { min_lat = lat } if lon < min_lon { min_lon = lon } if lat > max_lat { max_lat = lat } if lon > max_lon { max_lon = lon } if !(first) { fmt.Fprintf(response, " ,") } else { first = false fmt.Fprintf(response, " ") } fmt.Fprintf(response, "[%d, %s, %s, %s, %d]\n", a, s[8], s[9], s[10], vbat2percent(s[10])) } } } fmt.Fprintf(response, " ],\n") fmt.Fprintf(response, " \"min_lat\": %f,\n", min_lat) fmt.Fprintf(response, " \"min_lon\": %f,\n", min_lon) fmt.Fprintf(response, " \"max_lat\": %f,\n", max_lat) fmt.Fprintf(response, " \"max_lon\": %f\n", max_lon) fmt.Fprintf(response, "}\n") } else { fmt.Fprintf(response, "{ \"msg\": \"Only available for logged in users\" }") } } func vbat2percent(vbat string) int { res := 0 i, err := strconv.Atoi(vbat) if err == nil { if i < 3400 { res = 1 } else if i < 3700 { res = 2 } else if i < 3850 { res = 3 } else if i < 4000 { res = 4 } else { res = 5 } } return res } 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) |> tail(n:10) |> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s") |> filter(fn: (r) => r._field == "lon" or r._field == "lat" or r._field == "vbat" or r._field == "fw") |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")`, 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) >= 12) && !(strings.HasPrefix(s[3], "_")) { t, err := time.Parse(time.RFC3339, s[3]) if err != nil { fmt.Printf("error converting time: %s\n", s[3]) continue } res.Fw = s[8] res.Timestamp = t.In(location).Format("02.01.2006 15:04") if s[9] != "0" { res.Lat = s[9] res.PosTimestamp = t.In(location).Format("02.01.2006 15:04") } if s[10] != "0" { res.Lon = s[10] res.PosTimestamp = t.In(location).Format("02.01.2006 15:04") } res.BatteryPercent = s[11] } } res.Deveui = deveui res.DeveuiJS = template.JS(deveui) res.LatJS = template.JS(res.Lat) res.LonJS = template.JS(res.Lon) res.Alias = getDevAlias(deveui) res.Readonly = false res.ActiveUntil = getActiveUntil(deveui) res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil) fmt.Printf("Last Metrics: %v\n", res) return res }