refactoring & generic weatherdata

This commit is contained in:
Joel Schmid 2021-04-03 23:32:37 +02:00
parent 7fb0ebff20
commit 641c05afc6
4 changed files with 91 additions and 110 deletions

View file

@ -107,6 +107,8 @@ func (api *weatherRestApi) addDataHandler(w http.ResponseWriter, r *http.Request
return return
} }
fmt.Println(r.Body)
var data storage.WeatherData var data storage.WeatherData
err := json.NewDecoder(r.Body).Decode(&data) err := json.NewDecoder(r.Body).Decode(&data)
if err != nil { if err != nil {
@ -114,6 +116,8 @@ func (api *weatherRestApi) addDataHandler(w http.ResponseWriter, r *http.Request
return return
} }
fmt.Println(data)
err = api.addNewWeatherData(data) err = api.addNewWeatherData(data)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)

View file

@ -33,11 +33,11 @@ func (storage *influxStorage) Save(data WeatherData) error {
tags := map[string]string{ tags := map[string]string{
"sensorId": data.SensorId.String()} "sensorId": data.SensorId.String()}
fields := map[string]interface{}{ fields := make(map[string]interface{})
"temperature": data.Temperature,
"humidity": data.Humidity, for k, v := range data.Values {
"pressure": data.Pressure, fields[string(k)] = v
"co2level": data.CO2Level} }
datapoint := influxdb2.NewPoint(storage.measurement, datapoint := influxdb2.NewPoint(storage.measurement,
tags, tags,
@ -46,7 +46,6 @@ func (storage *influxStorage) Save(data WeatherData) error {
writeAPI := storage.client.WriteAPI(storage.config.Organization, storage.config.Bucket) writeAPI := storage.client.WriteAPI(storage.config.Organization, storage.config.Bucket)
writeAPI.WritePoint(datapoint) writeAPI.WritePoint(datapoint)
log.Print("Written weather data point to influx-db")
return nil return nil
} }
@ -61,24 +60,11 @@ func (storage *influxStorage) createFluxQuery(query *WeatherQuery) string {
fields := "" fields := ""
concat := "" concat := ""
if query.Temperature { for _, sensorValueType := range GetSensorValueTypes() {
fields = fmt.Sprintf("%v %v r._field == \"temperature\"", fields, concat) if query.Values[sensorValueType] {
concat = "or" fields = fmt.Sprintf("%v %v r._field == \"%v\"", fields, concat, string(sensorValueType))
} concat = "or"
}
if query.Humidity {
fields = fmt.Sprintf("%v %v r._field == \"humidity\"", fields, concat)
concat = "or"
}
if query.Pressure {
fields = fmt.Sprintf("%v %v r._field == \"pressure\"", fields, concat)
concat = "or"
}
if query.Co2Level {
fields = fmt.Sprintf("%v %v r._field == \"co2level\"", fields, concat)
concat = "or"
} }
fields = fmt.Sprintf(" and ( %v )", fields) fields = fmt.Sprintf(" and ( %v )", fields)
@ -112,17 +98,10 @@ func (storage *influxStorage) executeFluxQuery(query string) ([]*WeatherData, er
data, contained := containsWeatherData(queryResults, sensorId, timestamp) data, contained := containsWeatherData(queryResults, sensorId, timestamp)
if result.Record().Field() == "temperature" { for _, sensorValueType := range GetSensorValueTypes() {
data.Temperature = result.Record().Value().(float64) if result.Record().Field() == string(sensorValueType) {
} data.Values[sensorValueType] = result.Record().Value().(float64)
if result.Record().Field() == "pressure" { }
data.Pressure = result.Record().Value().(float64)
}
if result.Record().Field() == "humidity" {
data.Humidity = result.Record().Value().(float64)
}
if result.Record().Field() == "co2level" {
data.CO2Level = result.Record().Value().(float64)
} }
if !contained { if !contained {
@ -142,6 +121,7 @@ func containsWeatherData(weatherData []*WeatherData, sensorId uuid.UUID, timesta
} }
} }
var newData WeatherData var newData WeatherData
newData.Values = make(map[SensorValueType]float64)
return &newData, false return &newData, false
} }

View file

@ -10,6 +10,19 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
type SensorValueType string
const (
Temperature SensorValueType = "temperature"
Pressure SensorValueType = "pressure"
Humidity SensorValueType = "humidity"
Co2Level SensorValueType = "co2level"
)
func GetSensorValueTypes() []SensorValueType {
return []SensorValueType{Temperature, Pressure, Humidity, Co2Level}
}
//WeatherStorage interface for different storage-implementations of weather data //WeatherStorage interface for different storage-implementations of weather data
type WeatherStorage interface { type WeatherStorage interface {
Save(WeatherData) error Save(WeatherData) error
@ -27,38 +40,37 @@ type SensorRegistry interface {
//WeatherData type //WeatherData type
type WeatherData struct { type WeatherData struct {
Humidity float64 `json:"humidity"` Values map[SensorValueType]float64
Pressure float64 `json:"pressure"` SensorId uuid.UUID `json:"sensorId"`
Temperature float64 `json:"temperature"` TimeStamp time.Time `json:"timestamp"`
CO2Level float64 `json:"co2level"`
SensorId uuid.UUID `json:"sensorId"`
TimeStamp time.Time `json:"timestamp"`
} }
func (data *WeatherData) GetQueriedValues(query *WeatherQuery) map[string]string { func (data *WeatherData) OnlyQueriedValues(query *WeatherQuery) *WeatherData {
result := map[string]string{ for _, sensorValueType := range GetSensorValueTypes() {
if !query.Values[sensorValueType] {
delete(data.Values, sensorValueType)
}
}
return data
}
func (data *WeatherData) ToStringMap() map[string]string {
mappedData := map[string]string{
"sensorId": data.SensorId.String(), "sensorId": data.SensorId.String(),
"timestamp": data.TimeStamp.String(), "timeStamp": data.TimeStamp.String(),
} }
if query.Temperature {
result["temperature"] = strconv.FormatFloat(data.Temperature, 'f', -1, 32) for sensorValueType, value := range data.Values {
mappedData[string(sensorValueType)] = strconv.FormatFloat(value, 'f', -1, 64)
} }
if query.Pressure {
result["pressure"] = strconv.FormatFloat(data.Pressure, 'f', -1, 32) return mappedData
}
if query.Co2Level {
result["co2level"] = strconv.FormatFloat(data.CO2Level, 'f', -1, 32)
}
if query.Humidity {
result["humidity"] = strconv.FormatFloat(data.Humidity, 'f', -1, 32)
}
return result
} }
func GetOnlyQueriedFields(dataPoints []*WeatherData, query *WeatherQuery) []map[string]string { func GetOnlyQueriedFields(dataPoints []*WeatherData, query *WeatherQuery) []map[string]string {
var result []map[string]string var result []map[string]string
for _, data := range dataPoints { for _, data := range dataPoints {
result = append(result, data.GetQueriedValues(query)) result = append(result, data.OnlyQueriedValues(query).ToStringMap())
} }
return result return result
} }
@ -73,23 +85,20 @@ type WeatherSensor struct {
} }
type WeatherQuery struct { type WeatherQuery struct {
Start time.Time Start time.Time
End time.Time End time.Time
SensorId uuid.UUID SensorId uuid.UUID
Temperature bool Values map[SensorValueType]bool
Humidity bool
Pressure bool
Co2Level bool
} }
func (data *WeatherQuery) Init() { func (query *WeatherQuery) Init() {
data.Start = time.Now().Add(-1 * time.Hour * 24 * 14) query.Start = time.Now().Add(-1 * time.Hour * 24 * 14)
data.End = time.Now() query.End = time.Now()
data.SensorId = uuid.Nil query.SensorId = uuid.Nil
data.Temperature = true query.Values = make(map[SensorValueType]bool)
data.Humidity = true for _, sensorValueType := range GetSensorValueTypes() {
data.Pressure = true query.Values[sensorValueType] = true
data.Co2Level = true }
} }
func ParseFromUrlQuery(query url.Values) (*WeatherQuery, error) { func ParseFromUrlQuery(query url.Values) (*WeatherQuery, error) {
@ -98,10 +107,6 @@ func ParseFromUrlQuery(query url.Values) (*WeatherQuery, error) {
start := query.Get("start") start := query.Get("start")
end := query.Get("end") end := query.Get("end")
temperature := query.Get("temperature")
humidity := query.Get("humidity")
pressure := query.Get("pressure")
co2level := query.Get("co2level")
if len(start) != 0 { if len(start) != 0 {
if tval, err := time.Parse(time.RFC3339, start); err == nil { if tval, err := time.Parse(time.RFC3339, start); err == nil {
@ -121,20 +126,11 @@ func ParseFromUrlQuery(query url.Values) (*WeatherQuery, error) {
} }
} }
if bval, err := strconv.ParseBool(temperature); err == nil { for _, sensorValueType := range GetSensorValueTypes() {
result.Temperature = bval queryParam := query.Get(string(sensorValueType))
} if bval, err := strconv.ParseBool(queryParam); err == nil {
result.Values[sensorValueType] = bval
if bval, err := strconv.ParseBool(humidity); err == nil { }
result.Humidity = bval
}
if bval, err := strconv.ParseBool(pressure); err == nil {
result.Pressure = bval
}
if bval, err := strconv.ParseBool(co2level); err == nil {
result.Co2Level = bval
} }
return result, nil return result, nil
@ -144,10 +140,10 @@ func ParseFromUrlQuery(query url.Values) (*WeatherQuery, error) {
func NewRandomWeatherData(sensorId uuid.UUID) WeatherData { func NewRandomWeatherData(sensorId uuid.UUID) WeatherData {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
var data WeatherData var data WeatherData
data.Humidity = rand.Float64() * 100 data.Values[Humidity] = rand.Float64() * 100
data.Pressure = rand.Float64()*80 + 960 data.Values[Pressure] = rand.Float64()*80 + 960
data.Temperature = rand.Float64()*40 - 5 data.Values[Temperature] = rand.Float64()*40 - 5
data.CO2Level = rand.Float64()*50 + 375 data.Values[Co2Level] = rand.Float64()*50 + 375
data.SensorId = sensorId data.SensorId = sensorId
data.TimeStamp = time.Now() data.TimeStamp = time.Now()
return data return data

View file

@ -4,7 +4,6 @@ import (
"log" "log"
"regexp" "regexp"
"strconv" "strconv"
"strings"
"time" "time"
"weather-data/config" "weather-data/config"
"weather-data/storage" "weather-data/storage"
@ -13,7 +12,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
var mqttTopicRegexPattern = "(^sensor/)([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(/(temp|pressure|humidity|co2level)$)" var mqttTopicRegexPattern = "(^sensor)/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})/(.*)"
var regexTopic *regexp.Regexp = regexp.MustCompile(mqttTopicRegexPattern) var regexTopic *regexp.Regexp = regexp.MustCompile(mqttTopicRegexPattern)
@ -79,26 +78,28 @@ func (source *mqttWeatherSource) mqttMessageHandler() mqtt.MessageHandler {
if !found { if !found {
lastWeatherData = new(storage.WeatherData) lastWeatherData = new(storage.WeatherData)
lastWeatherData.Values = make(map[storage.SensorValueType]float64)
lastWeatherData.SensorId = sensorId lastWeatherData.SensorId = sensorId
source.lastWeatherDataPoints = append(source.lastWeatherDataPoints, lastWeatherData) source.lastWeatherDataPoints = append(source.lastWeatherDataPoints, lastWeatherData)
} }
if strings.HasSuffix(msg.Topic(), "pressure") { value, err := strconv.ParseFloat(string(msg.Payload()), 64)
lastWeatherData.Pressure, _ = strconv.ParseFloat(string(msg.Payload()), 64) if err != nil {
lastWeatherData.TimeStamp = time.Now() return
} }
if strings.HasSuffix(msg.Topic(), "temp") {
lastWeatherData.Temperature, _ = strconv.ParseFloat(string(msg.Payload()), 64) sensorValueType := storage.SensorValueType(regexTopic.FindStringSubmatch(msg.Topic())[3])
lastWeatherData.TimeStamp = time.Now() lastWeatherData.Values[sensorValueType] = value
} lastWeatherData.TimeStamp = time.Now()
if strings.HasSuffix(msg.Topic(), "humidity") {
lastWeatherData.Temperature, _ = strconv.ParseFloat(string(msg.Payload()), 64) /* only use predefined sensorValueTypes
lastWeatherData.TimeStamp = time.Now() for _, sensorValueType := range storage.GetSensorValueTypes() {
} if strings.HasSuffix(msg.Topic(), string(sensorValueType)) {
if strings.HasSuffix(msg.Topic(), "co2level") { lastWeatherData.Values[sensorValueType], _ = strconv.ParseFloat(string(msg.Payload()), 64)
lastWeatherData.CO2Level, _ = strconv.ParseFloat(string(msg.Payload()), 64) lastWeatherData.TimeStamp = time.Now()
lastWeatherData.TimeStamp = time.Now() }
} }
*/
} }
} }