From 072a902376b23580d7f499050b6e335812146bc5 Mon Sep 17 00:00:00 2001 From: Markus Pesch Date: Wed, 26 Jun 2019 23:16:26 +0200 Subject: [PATCH] fix(pkg/logfile): read and write logfiles from json with measured value interface --- cmd/temperature/list.go | 2 +- cmd/temperature/read.go | 2 +- pkg/daemon/daemon.go | 2 +- pkg/logfile/json.go | 238 +++++++++++++++++++++------------------- pkg/logfile/logfile.go | 32 +++--- 5 files changed, 145 insertions(+), 131 deletions(-) diff --git a/cmd/temperature/list.go b/cmd/temperature/list.go index 350c12d..37cf766 100644 --- a/cmd/temperature/list.go +++ b/cmd/temperature/list.go @@ -23,7 +23,7 @@ var listTemperatureCmd = &cobra.Command{ log.Fatalln(err) } - logfile := logfile.New(cnf) + logfile := logfile.New(cnf.Device.TemperatureLogfile) measuredValues, err := logfile.Read() if err != nil { diff --git a/cmd/temperature/read.go b/cmd/temperature/read.go index 9fb943d..8b6c09c 100644 --- a/cmd/temperature/read.go +++ b/cmd/temperature/read.go @@ -50,7 +50,7 @@ var readTemperatureCmd = &cobra.Command{ cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout) if logs { - logfile := logfile.New(cnf) + logfile := logfile.New(cnf.Device.TemperatureLogfile) err := logfile.Append(compression, measuredValues) if err != nil { rgbled.Error(rgbLEDs) diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index b4bfa2e..85aaa7f 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -33,7 +33,7 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress ctx := context.Background() childContext, cancel := context.WithCancel(ctx) - logfile := logfile.New(cnf) + logfile := logfile.New(cnf.Device.TemperatureLogfile) measuredValuesCache := make([]types.MeasuredValue, 0) diff --git a/pkg/logfile/json.go b/pkg/logfile/json.go index d6116bc..b08de28 100644 --- a/pkg/logfile/json.go +++ b/pkg/logfile/json.go @@ -3,19 +3,18 @@ package logfile import ( "encoding/json" "fmt" + "io/ioutil" + "log" "os" - "sync" - - "github.com/go-flucky/flucky/pkg/internal/collect" - "github.com/go-flucky/flucky/pkg/internal/prittyprint" - - "github.com/go-flucky/flucky/pkg/config" + "path/filepath" + "strconv" + "time" "github.com/go-flucky/flucky/pkg/types" ) type jsonLogfile struct { - cnf *config.Configuration + logfile string } func (jl *jsonLogfile) Append(compression bool, measuredValues []types.MeasuredValue) error { @@ -38,151 +37,166 @@ func (jl *jsonLogfile) Append(compression bool, measuredValues []types.MeasuredV func (jl *jsonLogfile) Read() ([]types.MeasuredValue, error) { + if _, err := os.Stat(jl.logfile); os.IsNotExist(err) { + return nil, fmt.Errorf("%v: %v", errorLogfileNotFound, jl.logfile) + } + + unmarshaledMeasuredValues := make([]interface{}, 0) + + bytes, err := ioutil.ReadFile(jl.logfile) + if err != nil { + return nil, fmt.Errorf("%v %v: %v", errorLogfileRead, jl.logfile, err) + } + + err = json.Unmarshal(bytes, &unmarshaledMeasuredValues) + if err != nil { + return nil, fmt.Errorf("%v %v: %v", errorLogfileUnmarshal, jl.logfile, err) + } + measuredValues := make([]types.MeasuredValue, 0) - humidityValues, err := jl.readHumidities() - if err != nil { - return nil, err - } - measuredValues = append(measuredValues, humidityValues...) + for _, unmarshaledMeasuredValue := range unmarshaledMeasuredValues { + mappedMeasuredValue, ok := unmarshaledMeasuredValue.(map[string]interface{}) + if !ok { + log.Println("Can not parse into map") + continue + } + + if _, ok := mappedMeasuredValue["temperature_id"]; ok { + temperature, err := jl.transformTemperature(mappedMeasuredValue) + if err != nil { + log.Printf("Can not unmarshal temperature from map: %v", err) + continue + } + measuredValues = append(measuredValues, temperature) + } + + if _, ok := mappedMeasuredValue["humidity_id"]; ok { + humidity, err := jl.transformHumidity(mappedMeasuredValue) + if err != nil { + log.Printf("Can not unmarshal humidity from map: %v", err) + continue + } + measuredValues = append(measuredValues, humidity) + } - temperatureValues, err := jl.readTemperatures() - if err != nil { - return nil, err } - measuredValues = append(measuredValues, temperatureValues...) return measuredValues, nil } -func (jl *jsonLogfile) readHumidities() ([]types.MeasuredValue, error) { +func (jl *jsonLogfile) transformHumidity(mappedMeasuredValue map[string]interface{}) (types.MeasuredValue, error) { - if _, err := os.Stat(jl.cnf.Device.HumidityLogfile); os.IsNotExist(err) { - return nil, fmt.Errorf("%v: %v", errorLogfileNotFound, jl.cnf.Device.HumidityLogfile) + humidity := &types.Humidity{ + HumidityID: mappedMeasuredValue["humidity_id"].(string), + SensorID: mappedMeasuredValue["sensor_id"].(string), + CreationDate: nil, + UpdateDate: nil, } - humidities := make([]*types.Humidity, 0) - - f, err := os.Open(jl.cnf.Device.HumidityLogfile) + humidityValue, err := strconv.ParseFloat(mappedMeasuredValue["humidity_value"].(string), 64) if err != nil { - return nil, fmt.Errorf("%v: %v", errorLogfileOpen, jl.cnf.Device.HumidityLogfile) - + return nil, fmt.Errorf("%v for humidity value. HumidityID %v: %v", errorParseFloat, mappedMeasuredValue["humidity_id"].(string), err) } - defer f.Close() + humidity.HumidityValue = humidityValue - if err = json.NewDecoder(f).Decode(&humidities); err != nil { - return nil, fmt.Errorf("%v: %v", errorLogfileDecode, err) + humidityFromDate, err := time.Parse(timeFormat, mappedMeasuredValue["humidity_from_date"].(string)) + if err != nil { + return nil, fmt.Errorf("%v for humidity from date. HumidityID %v: %v", errorParseTime, mappedMeasuredValue["humidity_id"].(string), err) + } + humidity.HumidityFromDate = humidityFromDate + + humidityTillDate, err := time.Parse(timeFormat, mappedMeasuredValue["humidity_till_date"].(string)) + if err != nil { + return nil, fmt.Errorf("%v for humidity till date. HumidityID %v: %v", errorParseTime, mappedMeasuredValue["humidity_id"].(string), err) + } + humidity.HumidityTillDate = humidityTillDate + + if mappedMeasuredValue["creation_date"] != nil { + creationDate, err := time.Parse(timeFormat, mappedMeasuredValue["creation_date"].(string)) + if err != nil { + return nil, fmt.Errorf("%v for creation date. HumidityID %v: %v", errorParseTime, mappedMeasuredValue["humidity_id"].(string), err) + } + humidity.CreationDate = &creationDate } - measuredValues := make([]types.MeasuredValue, 0) - - for _, humidity := range humidities { - measuredValues = append(measuredValues, humidity) + if mappedMeasuredValue["update_date"] != nil { + updateDate, err := time.Parse(timeFormat, mappedMeasuredValue["update_date"].(string)) + if err != nil { + return nil, fmt.Errorf("%v for update date. HumidityID %v: %v", errorParseTime, mappedMeasuredValue["humidity_id"].(string), err) + } + humidity.UpdateDate = &updateDate } - return measuredValues, nil - + return humidity, nil } -func (jl *jsonLogfile) readTemperatures() ([]types.MeasuredValue, error) { - if _, err := os.Stat(jl.cnf.Device.TemperatureLogfile); os.IsNotExist(err) { - return nil, fmt.Errorf("%v: %v", errorLogfileNotFound, jl.cnf.Device.TemperatureLogfile) +func (jl *jsonLogfile) transformTemperature(mappedMeasuredValue map[string]interface{}) (types.MeasuredValue, error) { + temperature := &types.Temperature{ + TemperatureID: mappedMeasuredValue["temperature_id"].(string), + SensorID: mappedMeasuredValue["sensor_id"].(string), + CreationDate: nil, + UpdateDate: nil, } - temperatures := make([]*types.Temperature, 0) - - f, err := os.Open(jl.cnf.Device.TemperatureLogfile) + temperatureValue, err := strconv.ParseFloat(mappedMeasuredValue["temperature_value"].(string), 64) if err != nil { - return nil, fmt.Errorf("%v: %v", errorLogfileOpen, jl.cnf.Device.TemperatureLogfile) - + return nil, fmt.Errorf("%v for temperature value. TemperatureID %v: %v", errorParseFloat, mappedMeasuredValue["temperature_id"].(string), err) } - defer f.Close() + temperature.TemperatureValue = temperatureValue - if err := json.NewDecoder(f).Decode(&temperatures); err != nil { - return nil, fmt.Errorf("%v: %v", errorLogfileDecode, err) + temperatureFromDate, err := time.Parse(timeFormat, mappedMeasuredValue["temperature_from_date"].(string)) + if err != nil { + return nil, fmt.Errorf("%v for temperature from date. TemperatureID %v: %v", errorParseTime, mappedMeasuredValue["temperature_id"].(string), err) + } + temperature.TemperatureFromDate = temperatureFromDate + + temperatureTillDate, err := time.Parse(timeFormat, mappedMeasuredValue["temperature_till_date"].(string)) + if err != nil { + return nil, fmt.Errorf("%v for temperature till date. TemperatureID %v: %v", errorParseTime, mappedMeasuredValue["temperature_id"].(string), err) + } + temperature.TemperatureTillDate = temperatureTillDate + + if mappedMeasuredValue["creation_date"] != nil { + creationDate, err := time.Parse(timeFormat, mappedMeasuredValue["creation_date"].(string)) + if err != nil { + return nil, fmt.Errorf("%v for creation date. TemperatureID %v: %v", errorParseTime, mappedMeasuredValue["temperature_id"].(string), err) + } + temperature.CreationDate = &creationDate } - measuredValues := make([]types.MeasuredValue, 0) - - for _, temperature := range temperatures { - measuredValues = append(measuredValues, temperature) + if mappedMeasuredValue["update_date"] != nil { + updateDate, err := time.Parse(timeFormat, mappedMeasuredValue["update_date"].(string)) + if err != nil { + return nil, fmt.Errorf("%v for update date. TemperatureID %v: %v", errorParseTime, mappedMeasuredValue["temperature_id"].(string), err) + } + temperature.UpdateDate = &updateDate } - return measuredValues, nil - + return temperature, nil } func (jl *jsonLogfile) Write(measuredValues []types.MeasuredValue) error { - humidities := make([]*types.Humidity, 0) - temperatures := make([]*types.Temperature, 0) - for _, measuredValue := range measuredValues { - switch v := measuredValue.(type) { - case *types.Humidity: - humidities = append(humidities, v) - case *types.Temperature: - temperatures = append(temperatures, v) - default: - return fmt.Errorf("%v", errorTypeSwitch) + if _, err := os.Stat(filepath.Dir(jl.logfile)); os.IsNotExist(err) { + if err := os.MkdirAll(filepath.Dir(jl.logfile), 755); err != nil { + return fmt.Errorf("Directory for the logfile can not be created: %v", err) } } - errorChannel := make(chan error, 0) + f, err := os.Create(jl.logfile) + if err != nil { + return fmt.Errorf("%v %v: %v", errorLogfileCreate, jl.logfile, err) + } - wg := new(sync.WaitGroup) - wg.Add(2) - - go jl.writeHumidities(humidities, errorChannel, wg) - go jl.writeTemperatures(temperatures, errorChannel, wg) - - wg.Wait() - - errors := collect.Errors(errorChannel) - if len(errors) > 0 { - return prittyprint.FormatErrors(errors) + jsonEncoder := json.NewEncoder(f) + jsonEncoder.SetIndent("", " ") + err = jsonEncoder.Encode(measuredValues) + if err != nil { + return fmt.Errorf("%v %v: %v", errorLogfileEncode, jl.logfile, err) } return nil } - -func (jl *jsonLogfile) writeHumidities(humidities []*types.Humidity, errorChannel chan<- error, wg *sync.WaitGroup) { - defer wg.Done() - - f, err := os.Create(jl.cnf.Device.HumidityLogfile) - if err != nil { - errorChannel <- fmt.Errorf("%v: %v", errorLogfileCreate, jl.cnf.Device.HumidityLogfile) - return - } - defer f.Close() - - jsonEncoder := json.NewEncoder(f) - jsonEncoder.SetIndent("", " ") - err = jsonEncoder.Encode(humidities) - if err != nil { - errorChannel <- fmt.Errorf("%v: %v", errorLogfileEncode, err) - return - } -} - -func (jl *jsonLogfile) writeTemperatures(temperatures []*types.Temperature, errorChannel chan<- error, wg *sync.WaitGroup) { - defer wg.Done() - - f, err := os.Create(jl.cnf.Device.TemperatureLogfile) - if err != nil { - errorChannel <- fmt.Errorf("%v: %v", errorLogfileCreate, jl.cnf.Device.TemperatureLogfile) - return - } - defer f.Close() - - //writeCreationDate(temperatures) - - jsonEncoder := json.NewEncoder(f) - jsonEncoder.SetIndent("", " ") - err = jsonEncoder.Encode(temperatures) - if err != nil { - errorChannel <- fmt.Errorf("%v: %v", errorLogfileEncode, err) - return - } -} diff --git a/pkg/logfile/logfile.go b/pkg/logfile/logfile.go index f38ff39..60ae0ac 100644 --- a/pkg/logfile/logfile.go +++ b/pkg/logfile/logfile.go @@ -1,11 +1,12 @@ package logfile import ( - "github.com/go-flucky/flucky/pkg/config" + "path/filepath" + "time" ) // var validUUID = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") -// var timeFormat = "2006-01-02 15:04:05.999999999 -0700" +var timeFormat = time.RFC3339 // // AppendTemperatures with temperature values from a logfile. As additional // // option it's possible to compress the temperature data. @@ -77,30 +78,29 @@ import ( // New returns a log file with basic functions for reading and writing data. The // file extension of the logfile is taken into account to format the logfile // into the correct format. -func New(cnf *config.Configuration) Logfile { +func New(logfile string) Logfile { - // switch ext { + ext := filepath.Join(logfile) + + switch ext { // case ".csv": // return &csvLogfile{ // logfile: logfile, // } - // case ".json": - // return &jsonLogfile{ - // cnf: cnf, - // } + case ".json": + return &jsonLogfile{ + logfile: logfile, + } // case ".xml": // return &xmlLogfile{ // logfile: logfile, // } - // default: - // return &jsonLogfile{ - // logfile: logfile, - // } - // } - - return &jsonLogfile{ - cnf: cnf, + default: + return &jsonLogfile{ + logfile: logfile, + } } + } // // SplittTemperatures into multiple arrays. The Size can be defined by