refactoring

This commit is contained in:
Joel Schmid 2021-04-05 11:39:55 +02:00
parent 3983a91475
commit 5aed4560e9
10 changed files with 167 additions and 143 deletions

View file

@ -31,9 +31,10 @@ func NewRestAPI(connection string, weatherStorage storage.WeatherStorage, sensor
//Start a new Rest-API instance
func (api *weatherRestApi) Start() error {
handler := api.handleRequests()
return http.ListenAndServe(api.connection, handler) // http.ListenAndServe(api.connection, handler)
return http.ListenAndServe(api.connection, handler)
}
//Close the rest api
func (api *weatherRestApi) Close() {
}
@ -77,24 +78,24 @@ func (api *weatherRestApi) getData(w http.ResponseWriter, r *http.Request) {
data, err := api.weaterStorage.GetData(query)
if err != nil {
http.Error(w, "", http.StatusBadRequest)
http.Error(w, "error executing query", http.StatusBadRequest)
return
}
res := storage.ToMap(storage.GetOnlyQueriedFields(data, query))
json.NewEncoder(w).Encode(res)
}
func (api *weatherRestApi) randomWeatherHandler(w http.ResponseWriter, r *http.Request) {
datapoint := storage.NewRandomWeatherData(uuid.Nil)
w.Header().Add("content-type", "application/json")
json.NewEncoder(w).Encode(datapoint)
json.NewEncoder(w).Encode(storage.NewRandomWeatherData())
}
func (api *weatherRestApi) randomWeatherListHandler(w http.ResponseWriter, r *http.Request) {
var datapoints = make([]storage.WeatherData, 0)
var datapoints = make([]*storage.WeatherData, 0)
for i := 0; i < 10; i++ {
datapoints = append(datapoints, storage.NewRandomWeatherData(uuid.Nil))
datapoints = append(datapoints, storage.NewRandomWeatherData())
}
w.Header().Add("content-type", "application/json")

View file

@ -1,7 +1,7 @@
package storage
import (
"fmt"
"errors"
"github.com/google/uuid"
)
@ -21,7 +21,7 @@ func (registry *inmemorySensorRegistry) RegisterSensorByName(name string) (*Weat
return nil, err
}
if exist {
return nil, fmt.Errorf("Sensorname already exists")
return nil, errors.New("sensorname already exists")
}
sensor := new(WeatherSensor)
sensor.Name = name
@ -45,7 +45,7 @@ func (registry *inmemorySensorRegistry) ResolveSensorById(sensorId uuid.UUID) (*
return s, nil
}
}
return nil, fmt.Errorf("sensor does not exist")
return nil, errors.New("sensor does not exist")
}
func (registry *inmemorySensorRegistry) ExistSensor(sensor *WeatherSensor) (bool, error) {

View file

@ -2,7 +2,7 @@ package storage
import (
"context"
"fmt"
"errors"
"log"
"time"
"weather-data/config"
@ -59,7 +59,7 @@ func (registry *mongodbSensorRegistry) RegisterSensorByName(name string) (*Weath
return nil, err
}
if exist {
return nil, fmt.Errorf("Sensorname already exists")
return nil, errors.New("sensorname already exists")
}
sensor := new(WeatherSensor)
sensor.Name = name
@ -96,7 +96,7 @@ func (registry *mongodbSensorRegistry) ResolveSensorById(sensorId uuid.UUID) (*W
return s, nil
}
}
return nil, fmt.Errorf("sensor does not exist")
return nil, errors.New("sensor does not exist")
}
func (registry *mongodbSensorRegistry) ExistSensor(sensor *WeatherSensor) (bool, error) {

View file

@ -0,0 +1,20 @@
package storage
import "github.com/google/uuid"
type SensorRegistry interface {
RegisterSensorByName(string) (*WeatherSensor, error)
ExistSensor(*WeatherSensor) (bool, error)
ResolveSensorById(uuid.UUID) (*WeatherSensor, error)
GetSensors() ([]*WeatherSensor, error)
Close() error
}
//WeatherSensor is the data for a new Sensorregistration
type WeatherSensor struct {
Name string
Id uuid.UUID
Location string
Longitude float64
Latitude float64
}

View file

@ -3,8 +3,6 @@ package storage
import (
"fmt"
"math/rand"
"net/url"
"strconv"
"time"
"github.com/google/uuid"
@ -28,28 +26,35 @@ func GetSensorValueTypes() []SensorValueType {
return []SensorValueType{Temperature, Pressure, Humidity, Co2Level}
}
//WeatherStorage interface for different storage-implementations of weather data
type WeatherStorage interface {
Save(WeatherData) error
GetData(*WeatherQuery) ([]*WeatherData, error)
Close() error
}
type SensorRegistry interface {
RegisterSensorByName(string) (*WeatherSensor, error)
ExistSensor(*WeatherSensor) (bool, error)
ResolveSensorById(uuid.UUID) (*WeatherSensor, error)
GetSensors() ([]*WeatherSensor, error)
Close() error
}
//WeatherData type
type WeatherData struct {
Values map[SensorValueType]float64
SensorId uuid.UUID `json:"sensorId"`
TimeStamp time.Time `json:"timestamp"`
SensorId uuid.UUID
TimeStamp time.Time
}
//NewRandomWeatherData creates random WeatherData
func NewRandomWeatherData() *WeatherData {
rand.Seed(time.Now().UnixNano())
var data = new(WeatherData)
data.Values = make(map[SensorValueType]float64)
data.Values[Humidity] = rand.Float64() * 100
data.Values[Pressure] = rand.Float64()*80 + 960
data.Values[Temperature] = rand.Float64()*40 - 5
data.Values[Co2Level] = rand.Float64()*50 + 375
data.SensorId = uuid.New()
data.TimeStamp = time.Now()
return data
}
//NewRandomWeatherData creates random WeatherData
func NewWeatherData() *WeatherData {
var data = new(WeatherData)
data.Values = make(map[SensorValueType]float64)
return data
}
//OnlyQueriedValues remove all values not contained by the WeatherQuery
func (data *WeatherData) OnlyQueriedValues(query *WeatherQuery) *WeatherData {
for _, sensorValueType := range GetSensorValueTypes() {
if !query.Values[sensorValueType] {
@ -59,19 +64,21 @@ func (data *WeatherData) OnlyQueriedValues(query *WeatherQuery) *WeatherData {
return data
}
//ToMap converts WeatherData to a map[string]interface{}
func (data *WeatherData) ToMap() map[string]interface{} {
mappedData := map[string]interface{}{
"sensorId": data.SensorId.String(),
"timeStamp": data.TimeStamp.String(),
SensorId: data.SensorId.String(),
TimeStamp: data.TimeStamp.String(),
}
for sensorValueType, value := range data.Values {
mappedData[string(sensorValueType)] = value //strconv.FormatFloat(value, 'f', -1, 64)
mappedData[string(sensorValueType)] = value
}
return mappedData
}
//FromMap converts a map[string]interface{} to WeatherData
func FromMap(value map[string]interface{}) (*WeatherData, error) {
var data = new(WeatherData)
data.Values = make(map[SensorValueType]float64)
@ -113,6 +120,7 @@ func FromMap(value map[string]interface{}) (*WeatherData, error) {
return data, nil
}
//GetOnlyQueriedFields execute onlyQueriedValues on WeatherData slice an return this
func GetOnlyQueriedFields(dataPoints []*WeatherData, query *WeatherQuery) []*WeatherData {
for _, data := range dataPoints {
data.OnlyQueriedValues(query)
@ -120,6 +128,7 @@ func GetOnlyQueriedFields(dataPoints []*WeatherData, query *WeatherQuery) []*Wea
return dataPoints
}
//ToMap mapps all WeatherData of a slice ToMap
func ToMap(dataPoints []*WeatherData) []map[string]interface{} {
var result = make([]map[string]interface{}, 0)
for _, data := range dataPoints {
@ -127,77 +136,3 @@ func ToMap(dataPoints []*WeatherData) []map[string]interface{} {
}
return result
}
//WeatherSensor is the data for a new Sensorregistration
type WeatherSensor struct {
Name string
Id uuid.UUID
Location string
Longitude float64
Latitude float64
}
type WeatherQuery struct {
Start time.Time
End time.Time
SensorId uuid.UUID
Values map[SensorValueType]bool
}
func (query *WeatherQuery) Init() {
query.Start = time.Now().Add(-1 * time.Hour * 24 * 14)
query.End = time.Now()
query.SensorId = uuid.Nil
query.Values = make(map[SensorValueType]bool)
for _, sensorValueType := range GetSensorValueTypes() {
query.Values[sensorValueType] = true
}
}
func ParseFromUrlQuery(query url.Values) (*WeatherQuery, error) {
result := new(WeatherQuery)
result.Init()
start := query.Get("start")
end := query.Get("end")
if len(start) != 0 {
if tval, err := time.Parse(time.RFC3339, start); err == nil {
result.Start = tval
} else if err != nil {
fmt.Println(err)
return nil, err
}
}
if len(end) != 0 {
if tval, err := time.Parse(time.RFC3339, end); err == nil {
result.End = tval
} else if err != nil {
fmt.Println(err)
return nil, err
}
}
for _, sensorValueType := range GetSensorValueTypes() {
queryParam := query.Get(string(sensorValueType))
if bval, err := strconv.ParseBool(queryParam); err == nil {
result.Values[sensorValueType] = bval
}
}
return result, nil
}
//NewRandomWeatherData creates random WeatherData with given Location
func NewRandomWeatherData(sensorId uuid.UUID) WeatherData {
rand.Seed(time.Now().UnixNano())
var data WeatherData
data.Values[Humidity] = rand.Float64() * 100
data.Values[Pressure] = rand.Float64()*80 + 960
data.Values[Temperature] = rand.Float64()*40 - 5
data.Values[Co2Level] = rand.Float64()*50 + 375
data.SensorId = sensorId
data.TimeStamp = time.Now()
return data
}

68
storage/weather-query.go Normal file
View file

@ -0,0 +1,68 @@
package storage
import (
"fmt"
"net/url"
"strconv"
"time"
"github.com/google/uuid"
)
type WeatherQuery struct {
Start time.Time
End time.Time
SensorId uuid.UUID
Values map[SensorValueType]bool
}
//NewWeatherQuery creates a new empty WeatherQuery
func NewWeatherQuery() *WeatherQuery {
query := new(WeatherQuery)
query.Values = make(map[SensorValueType]bool)
return query
}
func (query *WeatherQuery) Init() {
query.Start = time.Now().Add(-1 * time.Hour * 24 * 14)
query.End = time.Now()
query.SensorId = uuid.Nil
for _, sensorValueType := range GetSensorValueTypes() {
query.Values[sensorValueType] = true
}
}
func ParseFromUrlQuery(query url.Values) (*WeatherQuery, error) {
result := NewWeatherQuery()
result.Init()
start := query.Get("start")
end := query.Get("end")
if len(start) != 0 {
if tval, err := time.Parse(time.RFC3339, start); err == nil {
result.Start = tval
} else if err != nil {
fmt.Println(err)
return nil, err
}
}
if len(end) != 0 {
if tval, err := time.Parse(time.RFC3339, end); err == nil {
result.End = tval
} else if err != nil {
fmt.Println(err)
return nil, err
}
}
for _, sensorValueType := range GetSensorValueTypes() {
queryParam := query.Get(string(sensorValueType))
if bval, err := strconv.ParseBool(queryParam); err == nil {
result.Values[sensorValueType] = bval
}
}
return result, nil
}

View file

@ -0,0 +1,8 @@
package storage
//WeatherStorage interface for different storage-implementations of weather data
type WeatherStorage interface {
Save(WeatherData) error
GetData(*WeatherQuery) ([]*WeatherData, error)
Close() error
}

View file

@ -77,8 +77,7 @@ func (source *mqttWeatherSource) mqttMessageHandler() mqtt.MessageHandler {
lastWeatherData, found := source.getUnwrittenDatapoints(sensorId)
if !found {
lastWeatherData = new(storage.WeatherData)
lastWeatherData.Values = make(map[storage.SensorValueType]float64)
lastWeatherData = storage.NewWeatherData()
lastWeatherData.SensorId = sensorId
source.lastWeatherDataPoints = append(source.lastWeatherDataPoints, lastWeatherData)
}
@ -91,15 +90,6 @@ func (source *mqttWeatherSource) mqttMessageHandler() mqtt.MessageHandler {
sensorValueType := storage.SensorValueType(regexTopic.FindStringSubmatch(msg.Topic())[3])
lastWeatherData.Values[sensorValueType] = value
lastWeatherData.TimeStamp = time.Now()
/* only use predefined sensorValueTypes
for _, sensorValueType := range storage.GetSensorValueTypes() {
if strings.HasSuffix(msg.Topic(), string(sensorValueType)) {
lastWeatherData.Values[sensorValueType], _ = strconv.ParseFloat(string(msg.Payload()), 64)
lastWeatherData.TimeStamp = time.Now()
}
}
*/
}
}
@ -107,7 +97,7 @@ func (source *mqttWeatherSource) publishDataValues() {
for {
for len(source.lastWeatherDataPoints) != 0 {
current := *source.lastWeatherDataPoints[0]
diff := time.Now().Sub(current.TimeStamp)
diff := time.Since(current.TimeStamp)
if diff >= source.config.MinDistToLastValue {
if err := source.newWeatherData(current); err != nil {
log.Fatal(err)
@ -120,7 +110,6 @@ func (source *mqttWeatherSource) publishDataValues() {
}
time.Sleep(source.config.PublishInterval)
}
}
func (source *mqttWeatherSource) getUnwrittenDatapoints(sensorId uuid.UUID) (*storage.WeatherData, bool) {

View file

@ -0,0 +1,24 @@
package weathersource
import "weather-data/storage"
//WeatherSourceBase is the lowlevel-implementation of the WeatherSource interface, intended to used by highlevel-implementations
type WeatherSourceBase struct {
newWeatherDataCallbackFuncs []NewWeatherDataCallbackFunc
}
//AddNewWeatherDataCallback adds a new callbackMethod for incoming weather data
func (source *WeatherSourceBase) AddNewWeatherDataCallback(callback NewWeatherDataCallbackFunc) {
source.newWeatherDataCallbackFuncs = append(source.newWeatherDataCallbackFuncs, callback)
}
//NewWeatherData executes all newWeatherDataCallbackFuncs for this datapoint
func (source *WeatherSourceBase) NewWeatherData(datapoint storage.WeatherData) error {
for _, callback := range source.newWeatherDataCallbackFuncs {
err := callback(datapoint)
if err != nil {
return err
}
}
return nil
}

View file

@ -10,24 +10,3 @@ type WeatherSource interface {
AddNewWeatherDataCallback(NewWeatherDataCallbackFunc)
Close()
}
//WeatherSourceBase is the lowlevel-implementation of the WeatherSource interface, intended to used by highlevel-implementations
type WeatherSourceBase struct {
newWeatherDataCallbackFuncs []NewWeatherDataCallbackFunc
}
//AddNewWeatherDataCallback adds a new callbackMethod for incoming weather data
func (source *WeatherSourceBase) AddNewWeatherDataCallback(callback NewWeatherDataCallbackFunc) {
source.newWeatherDataCallbackFuncs = append(source.newWeatherDataCallbackFuncs, callback)
}
//NewWeatherData executes all newWeatherDataCallbackFuncs for this datapoint
func (source *WeatherSourceBase) NewWeatherData(datapoint storage.WeatherData) error {
for _, callback := range source.newWeatherDataCallbackFuncs {
err := callback(datapoint)
if err != nil {
return err
}
}
return nil
}