|  |  |  | @@ -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 | 
		
	
		
			
				|  |  |  |  | } | 
		
	
	
		
			
				
					
					| 
							
							
							
						 |  |  |   |