feat(pkg/logfile): add compression
This commit is contained in:
		| @@ -12,6 +12,7 @@ import ( | ||||
| 	"github.com/volker-raschek/flucky/pkg/sensor" | ||||
| ) | ||||
|  | ||||
| var compression bool | ||||
| var logs bool | ||||
|  | ||||
| var readTemperatureCmd = &cobra.Command{ | ||||
| @@ -39,7 +40,7 @@ var readTemperatureCmd = &cobra.Command{ | ||||
| 		cli.PrintTemperatures(temperatures, cnf, os.Stdout) | ||||
|  | ||||
| 		if logs { | ||||
| 			err := logfile.WriteTemperatures(cnf.Device.TemperatureLogfile, temperatures) | ||||
| 			err := logfile.WriteTemperatures(temperatures, cnf.Device.TemperatureLogfile, compression) | ||||
| 			if err != nil { | ||||
| 				log.Fatalln(err) | ||||
| 			} | ||||
| @@ -49,6 +50,6 @@ var readTemperatureCmd = &cobra.Command{ | ||||
|  | ||||
| func init() { | ||||
| 	temperatureCmd.AddCommand(readTemperatureCmd) | ||||
| 	// readTemperatureCmd.Flags().BoolVarP(&follow, "follow", "f", false, "Follow output") | ||||
| 	readTemperatureCmd.Flags().BoolVarP(&logs, "logs", "l", true, "Log temperature") | ||||
| 	readTemperatureCmd.Flags().BoolVarP(&compression, "compression", "c", true, "Compress measured with logged temperatures") | ||||
| } | ||||
|   | ||||
| @@ -17,7 +17,6 @@ var temperatureCmd = &cobra.Command{ | ||||
| // Execute a | ||||
| func InitCmd(cmd *cobra.Command, configPath string) { | ||||
| 	cnfPath = configPath | ||||
|  | ||||
| 	cmd.AddCommand(temperatureCmd) | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -6,15 +6,114 @@ import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sort" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/volker-raschek/flucky/pkg/types" | ||||
| ) | ||||
|  | ||||
| // Define the entry size for each logfile | ||||
| var humiditySplitBy = 10000 | ||||
| var templeratureSplitBy = 10000 | ||||
|  | ||||
| func WriteTemperatures(temperatureLogfile string, temperatures []*types.Temperature) error { | ||||
| // CompressTemperature compresses the temperatures from an array. It is checked | ||||
| // whether the measured temperature of a value corresponds to that of the | ||||
| // predecessor. If this is the case, the current value is discarded and the | ||||
| // validity date of the predecessor value is set to that of the current value. | ||||
| // No information is lost as a result. The validity period of the measured value | ||||
| // is thereby exclusively increased. | ||||
| func CompressTemperature(temperatures []*types.Temperature) []*types.Temperature { | ||||
| 	compressedTemperatures := make([]*types.Temperature, 0) | ||||
| 	lastTemperatureBySensors := make(map[string]*types.Temperature, 0) | ||||
|  | ||||
| 	// Sort all measured temperatures beforehand by the starting validity date to | ||||
| 	// avoid errors when compressing the temperatures. | ||||
| 	SortTemperatures(temperatures) | ||||
|  | ||||
| 	for _, temperature := range temperatures { | ||||
| 		if lastTemperatureBySensor, ok := lastTemperatureBySensors[temperature.SensorID]; ok { | ||||
| 			if lastTemperatureBySensor.TemperatureValue == temperature.TemperatureValue { | ||||
|  | ||||
| 				lastTemperatureBySensors[temperature.SensorID].TemperatureTillDate = temperature.TemperatureTillDate | ||||
|  | ||||
| 				now := time.Now() | ||||
| 				lastTemperatureBySensors[temperature.SensorID].UpdateDate = &now | ||||
| 			} else { | ||||
| 				compressedTemperatures = append(compressedTemperatures, lastTemperatureBySensors[temperature.SensorID]) | ||||
| 				lastTemperatureBySensors[temperature.SensorID] = temperature | ||||
| 			} | ||||
| 		} else { | ||||
| 			lastTemperatureBySensors[temperature.SensorID] = temperature | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Copy all remaining entries from the map into the array | ||||
| 	for _, lastTemperatureBySensor := range lastTemperatureBySensors { | ||||
| 		if lastTemperatureBySensor.UpdateDate == nil { | ||||
| 			now := time.Now() | ||||
| 			lastTemperatureBySensor.UpdateDate = &now | ||||
| 		} | ||||
| 		compressedTemperatures = append(compressedTemperatures, lastTemperatureBySensor) | ||||
| 	} | ||||
|  | ||||
| 	return compressedTemperatures | ||||
| } | ||||
|  | ||||
| func ReadTemperatures(temperatureLogfile string) ([]*types.Temperature, error) { | ||||
|  | ||||
| 	if _, err := os.Stat(temperatureLogfile); os.IsNotExist(err) { | ||||
| 		return nil, fmt.Errorf("Can not find temperature logfile %v", temperatureLogfile) | ||||
| 	} | ||||
|  | ||||
| 	temperatures := make([]*types.Temperature, 0) | ||||
|  | ||||
| 	f, err := os.Open(temperatureLogfile) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Can not open temperature logfile %v", temperatureLogfile) | ||||
| 	} | ||||
| 	defer f.Close() | ||||
|  | ||||
| 	temperatures, err = ReadTemperaturesCustom(f) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Can not read temperatures from logfile %v", temperatureLogfile) | ||||
| 	} | ||||
|  | ||||
| 	return temperatures, nil | ||||
| } | ||||
|  | ||||
| func ReadTemperaturesCustom(r io.Reader) ([]*types.Temperature, error) { | ||||
|  | ||||
| 	temperatures := make([]*types.Temperature, 0) | ||||
|  | ||||
| 	decoder := json.NewDecoder(r) | ||||
| 	err := decoder.Decode(&temperatures) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Can not decode temperatures from reader: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return temperatures, nil | ||||
| } | ||||
|  | ||||
| func SplittTemperatures(temperatures []*types.Temperature) [][]*types.Temperature { | ||||
| 	splittedTemperatures := make([][]*types.Temperature, 0) | ||||
| 	newTemperatures := make([]*types.Temperature, 0) | ||||
| 	for _, temperature := range temperatures { | ||||
| 		if len(newTemperatures) == templeratureSplitBy { | ||||
| 			splittedTemperatures = append(splittedTemperatures, newTemperatures) | ||||
| 			newTemperatures = make([]*types.Temperature, 0) | ||||
| 		} | ||||
| 		newTemperatures = append(newTemperatures, temperature) | ||||
| 	} | ||||
| 	splittedTemperatures = append(splittedTemperatures, newTemperatures) | ||||
| 	return splittedTemperatures | ||||
| } | ||||
|  | ||||
| func SortTemperatures(temperatures []*types.Temperature) { | ||||
| 	sort.SliceStable(temperatures, func(i int, j int) bool { | ||||
| 		return temperatures[i].TemperatureFromDate.Before(temperatures[j].TemperatureFromDate) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func WriteTemperatures(temperatures []*types.Temperature, temperatureLogfile string, compression bool) error { | ||||
|  | ||||
| 	allTemperatures := make([]*types.Temperature, 0) | ||||
|  | ||||
| @@ -53,7 +152,7 @@ func WriteTemperatures(temperatureLogfile string, temperatures []*types.Temperat | ||||
|  | ||||
| 	allTemperatures = append(allTemperatures, temperatures...) | ||||
|  | ||||
| 	err = WriteTemperaturesCustom(f, allTemperatures) | ||||
| 	err = WriteTemperaturesCustom(allTemperatures, f, compression) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("Can not write temperatures to logfile %v: %v", temperatureLogfile, err) | ||||
| 	} | ||||
| @@ -61,7 +160,14 @@ func WriteTemperatures(temperatureLogfile string, temperatures []*types.Temperat | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func WriteTemperaturesCustom(w io.Writer, temperatures []*types.Temperature) error { | ||||
| func WriteTemperaturesCustom(temperatures []*types.Temperature, w io.Writer, compression bool) error { | ||||
|  | ||||
| 	if compression { | ||||
| 		temperatures = CompressTemperature(temperatures) | ||||
| 	} | ||||
|  | ||||
| 	writeCreationDate(temperatures) | ||||
|  | ||||
| 	jsonEncoder := json.NewEncoder(w) | ||||
| 	jsonEncoder.SetIndent("", " ") | ||||
| 	err := jsonEncoder.Encode(temperatures) | ||||
| @@ -71,37 +177,9 @@ func WriteTemperaturesCustom(w io.Writer, temperatures []*types.Temperature) err | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func ReadTemperatures(temperatureLogfile string) ([]*types.Temperature, error) { | ||||
|  | ||||
| 	if _, err := os.Stat(temperatureLogfile); os.IsNotExist(err) { | ||||
| 		return nil, fmt.Errorf("Can not find temperature logfile %v", temperatureLogfile) | ||||
| func writeCreationDate(temperatures []*types.Temperature) { | ||||
| 	now := time.Now() | ||||
| 	for _, temperature := range temperatures { | ||||
| 		temperature.CreationDate = &now | ||||
| 	} | ||||
|  | ||||
| 	temperatures := make([]*types.Temperature, 0) | ||||
|  | ||||
| 	f, err := os.Open(temperatureLogfile) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Can not open temperature logfile %v", temperatureLogfile) | ||||
| 	} | ||||
| 	defer f.Close() | ||||
|  | ||||
| 	temperatures, err = ReadTemperaturesCustom(f) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Can not read temperatures from logfile %v", temperatureLogfile) | ||||
| 	} | ||||
|  | ||||
| 	return temperatures, nil | ||||
| } | ||||
|  | ||||
| func ReadTemperaturesCustom(r io.Reader) ([]*types.Temperature, error) { | ||||
|  | ||||
| 	temperatures := make([]*types.Temperature, 0) | ||||
|  | ||||
| 	decoder := json.NewDecoder(r) | ||||
| 	err := decoder.Decode(&temperatures) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("Can not decode temperatures from reader: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return temperatures, nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										20
									
								
								pkg/logfile/test/testTemperatures.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								pkg/logfile/test/testTemperatures.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| [ | ||||
|   { | ||||
|     "temperature_id": "0975e0ab-7023-4d28-a5a4-fbd2d0111364", | ||||
|     "temperature_value": "24.562", | ||||
|     "temperature_from_date": "2019-06-14T20:48:44.184066823+02:00", | ||||
|     "temperature_till_date": "2019-06-14T20:48:44.184068854+02:00", | ||||
|     "sensor_id": "84eac248-6927-4db6-b6f9-7891ce2d301e", | ||||
|     "creation_date": "0001-01-01T00:00:00Z", | ||||
|     "update_date": "0001-01-01T00:00:00Z" | ||||
|   }, | ||||
|   { | ||||
|     "temperature_id": "bfbcb239-28f2-413f-88b4-972d039ab9cd", | ||||
|     "temperature_value": "24.312", | ||||
|     "temperature_from_date": "2019-06-14T20:48:44.263861577+02:00", | ||||
|     "temperature_till_date": "2019-06-14T20:48:44.263863192+02:00", | ||||
|     "sensor_id": "efcd755e-82d1-4789-a50b-355b8735b8d8", | ||||
|     "creation_date": "0001-01-01T00:00:00Z", | ||||
|     "update_date": "0001-01-01T00:00:00Z" | ||||
|   } | ||||
| ] | ||||
| @@ -4,10 +4,11 @@ import "time" | ||||
|  | ||||
| // Temperature ... | ||||
| type Temperature struct { | ||||
| 	TemperatureID       string    `json:"temperature_id"` | ||||
| 	TemperatureValue    float64   `json:"temperature_value,string"` | ||||
| 	TemperatureFromDate time.Time `json:"temperature_from_date"` | ||||
| 	TemperatureTillDate time.Time `json:"temperature_till_date"` | ||||
| 	SensorID            string    `json:"sensor_id"` | ||||
| 	CreationDate        time.Time `json:"creation_date"` | ||||
| 	TemperatureID       string     `json:"temperature_id"` | ||||
| 	TemperatureValue    float64    `json:"temperature_value,string"` | ||||
| 	TemperatureFromDate time.Time  `json:"temperature_from_date"` | ||||
| 	TemperatureTillDate time.Time  `json:"temperature_till_date"` | ||||
| 	SensorID            string     `json:"sensor_id"` | ||||
| 	CreationDate        *time.Time `json:"creation_date"` | ||||
| 	UpdateDate          *time.Time `json:"update_date"` | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user