fix(pkg/logfile): read and write logfiles from json with measured value interface
This commit is contained in:
		| @@ -23,7 +23,7 @@ var listTemperatureCmd = &cobra.Command{ | |||||||
| 			log.Fatalln(err) | 			log.Fatalln(err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		logfile := logfile.New(cnf) | 		logfile := logfile.New(cnf.Device.TemperatureLogfile) | ||||||
|  |  | ||||||
| 		measuredValues, err := logfile.Read() | 		measuredValues, err := logfile.Read() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ var readTemperatureCmd = &cobra.Command{ | |||||||
| 		cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout) | 		cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout) | ||||||
|  |  | ||||||
| 		if logs { | 		if logs { | ||||||
| 			logfile := logfile.New(cnf) | 			logfile := logfile.New(cnf.Device.TemperatureLogfile) | ||||||
| 			err := logfile.Append(compression, measuredValues) | 			err := logfile.Append(compression, measuredValues) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				rgbled.Error(rgbLEDs) | 				rgbled.Error(rgbLEDs) | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress | |||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
| 	childContext, cancel := context.WithCancel(ctx) | 	childContext, cancel := context.WithCancel(ctx) | ||||||
|  |  | ||||||
| 	logfile := logfile.New(cnf) | 	logfile := logfile.New(cnf.Device.TemperatureLogfile) | ||||||
|  |  | ||||||
| 	measuredValuesCache := make([]types.MeasuredValue, 0) | 	measuredValuesCache := make([]types.MeasuredValue, 0) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,19 +3,18 @@ package logfile | |||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
| 	"sync" | 	"path/filepath" | ||||||
|  | 	"strconv" | ||||||
| 	"github.com/go-flucky/flucky/pkg/internal/collect" | 	"time" | ||||||
| 	"github.com/go-flucky/flucky/pkg/internal/prittyprint" |  | ||||||
|  |  | ||||||
| 	"github.com/go-flucky/flucky/pkg/config" |  | ||||||
|  |  | ||||||
| 	"github.com/go-flucky/flucky/pkg/types" | 	"github.com/go-flucky/flucky/pkg/types" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type jsonLogfile struct { | type jsonLogfile struct { | ||||||
| 	cnf *config.Configuration | 	logfile string | ||||||
| } | } | ||||||
|  |  | ||||||
| func (jl *jsonLogfile) Append(compression bool, measuredValues []types.MeasuredValue) error { | 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) { | 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) | 	measuredValues := make([]types.MeasuredValue, 0) | ||||||
|  |  | ||||||
| 	humidityValues, err := jl.readHumidities() | 	for _, unmarshaledMeasuredValue := range unmarshaledMeasuredValues { | ||||||
| 	if err != nil { | 		mappedMeasuredValue, ok := unmarshaledMeasuredValue.(map[string]interface{}) | ||||||
| 		return nil, err | 		if !ok { | ||||||
| 	} | 			log.Println("Can not parse into map") | ||||||
| 	measuredValues = append(measuredValues, humidityValues...) | 			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 | 	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) { | 	humidity := &types.Humidity{ | ||||||
| 		return nil, fmt.Errorf("%v: %v", errorLogfileNotFound, jl.cnf.Device.HumidityLogfile) | 		HumidityID:   mappedMeasuredValue["humidity_id"].(string), | ||||||
|  | 		SensorID:     mappedMeasuredValue["sensor_id"].(string), | ||||||
|  | 		CreationDate: nil, | ||||||
|  | 		UpdateDate:   nil, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	humidities := make([]*types.Humidity, 0) | 	humidityValue, err := strconv.ParseFloat(mappedMeasuredValue["humidity_value"].(string), 64) | ||||||
|  |  | ||||||
| 	f, err := os.Open(jl.cnf.Device.HumidityLogfile) |  | ||||||
| 	if err != nil { | 	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 { | 	humidityFromDate, err := time.Parse(timeFormat, mappedMeasuredValue["humidity_from_date"].(string)) | ||||||
| 		return nil, fmt.Errorf("%v: %v", errorLogfileDecode, err) | 	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) | 	if mappedMeasuredValue["update_date"] != nil { | ||||||
|  | 		updateDate, err := time.Parse(timeFormat, mappedMeasuredValue["update_date"].(string)) | ||||||
| 	for _, humidity := range humidities { | 		if err != nil { | ||||||
| 		measuredValues = append(measuredValues, humidity) | 			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) { | func (jl *jsonLogfile) transformTemperature(mappedMeasuredValue map[string]interface{}) (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) |  | ||||||
|  |  | ||||||
|  | 	temperature := &types.Temperature{ | ||||||
|  | 		TemperatureID: mappedMeasuredValue["temperature_id"].(string), | ||||||
|  | 		SensorID:      mappedMeasuredValue["sensor_id"].(string), | ||||||
|  | 		CreationDate:  nil, | ||||||
|  | 		UpdateDate:    nil, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	temperatures := make([]*types.Temperature, 0) | 	temperatureValue, err := strconv.ParseFloat(mappedMeasuredValue["temperature_value"].(string), 64) | ||||||
|  |  | ||||||
| 	f, err := os.Open(jl.cnf.Device.TemperatureLogfile) |  | ||||||
| 	if err != nil { | 	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 { | 	temperatureFromDate, err := time.Parse(timeFormat, mappedMeasuredValue["temperature_from_date"].(string)) | ||||||
| 		return nil, fmt.Errorf("%v: %v", errorLogfileDecode, err) | 	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) | 	if mappedMeasuredValue["update_date"] != nil { | ||||||
|  | 		updateDate, err := time.Parse(timeFormat, mappedMeasuredValue["update_date"].(string)) | ||||||
| 	for _, temperature := range temperatures { | 		if err != nil { | ||||||
| 		measuredValues = append(measuredValues, temperature) | 			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 { | func (jl *jsonLogfile) Write(measuredValues []types.MeasuredValue) error { | ||||||
| 	humidities := make([]*types.Humidity, 0) |  | ||||||
| 	temperatures := make([]*types.Temperature, 0) |  | ||||||
|  |  | ||||||
| 	for _, measuredValue := range measuredValues { | 	if _, err := os.Stat(filepath.Dir(jl.logfile)); os.IsNotExist(err) { | ||||||
| 		switch v := measuredValue.(type) { | 		if err := os.MkdirAll(filepath.Dir(jl.logfile), 755); err != nil { | ||||||
| 		case *types.Humidity: | 			return fmt.Errorf("Directory for the logfile can not be created: %v", err) | ||||||
| 			humidities = append(humidities, v) |  | ||||||
| 		case *types.Temperature: |  | ||||||
| 			temperatures = append(temperatures, v) |  | ||||||
| 		default: |  | ||||||
| 			return fmt.Errorf("%v", errorTypeSwitch) |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	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) | 	jsonEncoder := json.NewEncoder(f) | ||||||
| 	wg.Add(2) | 	jsonEncoder.SetIndent("", "  ") | ||||||
|  | 	err = jsonEncoder.Encode(measuredValues) | ||||||
| 	go jl.writeHumidities(humidities, errorChannel, wg) | 	if err != nil { | ||||||
| 	go jl.writeTemperatures(temperatures, errorChannel, wg) | 		return fmt.Errorf("%v %v: %v", errorLogfileEncode, jl.logfile, err) | ||||||
|  |  | ||||||
| 	wg.Wait() |  | ||||||
|  |  | ||||||
| 	errors := collect.Errors(errorChannel) |  | ||||||
| 	if len(errors) > 0 { |  | ||||||
| 		return prittyprint.FormatErrors(errors) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	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 |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| package logfile | package logfile | ||||||
|  |  | ||||||
| import ( | 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 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 | // // AppendTemperatures with temperature values from a logfile. As additional | ||||||
| // // option it's possible to compress the temperature data. | // // 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 | // 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 | // file extension of the logfile is taken into account to format the logfile | ||||||
| // into the correct format. | // 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": | 	// case ".csv": | ||||||
| 	// 	return &csvLogfile{ | 	// 	return &csvLogfile{ | ||||||
| 	// 		logfile: logfile, | 	// 		logfile: logfile, | ||||||
| 	// 	} | 	// 	} | ||||||
| 	// case ".json": | 	case ".json": | ||||||
| 	// 	return &jsonLogfile{ | 		return &jsonLogfile{ | ||||||
| 	// 		cnf: cnf, | 			logfile: logfile, | ||||||
| 	// 	} | 		} | ||||||
| 	// case ".xml": | 	// case ".xml": | ||||||
| 	// 	return &xmlLogfile{ | 	// 	return &xmlLogfile{ | ||||||
| 	// 		logfile: logfile, | 	// 		logfile: logfile, | ||||||
| 	// 	} | 	// 	} | ||||||
| 	// default: | 	default: | ||||||
| 	// 	return &jsonLogfile{ | 		return &jsonLogfile{ | ||||||
| 	// 		logfile: logfile, | 			logfile: logfile, | ||||||
| 	// 	} | 		} | ||||||
| 	// } |  | ||||||
|  |  | ||||||
| 	return &jsonLogfile{ |  | ||||||
| 		cnf: cnf, |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // // SplittTemperatures into multiple arrays. The Size can be defined by | // // SplittTemperatures into multiple arrays. The Size can be defined by | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user