fix(pkg/logfile): read and write logfiles from json with measured value interface
This commit is contained in:
		| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user