diff --git a/cmd/temperature/read.go b/cmd/temperature/read.go index e991938..150a182 100644 --- a/cmd/temperature/read.go +++ b/cmd/temperature/read.go @@ -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") } diff --git a/cmd/temperature/temperature.go b/cmd/temperature/temperature.go index ce9b373..4b6a2ed 100644 --- a/cmd/temperature/temperature.go +++ b/cmd/temperature/temperature.go @@ -17,7 +17,6 @@ var temperatureCmd = &cobra.Command{ // Execute a func InitCmd(cmd *cobra.Command, configPath string) { cnfPath = configPath - cmd.AddCommand(temperatureCmd) } diff --git a/pkg/logfile/logfile.go b/pkg/logfile/logfile.go index 8ed5841..3c327b3 100644 --- a/pkg/logfile/logfile.go +++ b/pkg/logfile/logfile.go @@ -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 } diff --git a/pkg/logfile/test/testTemperatures.json b/pkg/logfile/test/testTemperatures.json new file mode 100644 index 0000000..4a3ab0b --- /dev/null +++ b/pkg/logfile/test/testTemperatures.json @@ -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" + } +] \ No newline at end of file diff --git a/pkg/types/temperature.go b/pkg/types/temperature.go index 447a2a5..47197c9 100644 --- a/pkg/types/temperature.go +++ b/pkg/types/temperature.go @@ -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"` }