package logfile import ( "math" "path/filepath" "sort" "time" "github.com/go-flucky/flucky/pkg/types" ) // 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-02T15:04:05.999999Z07:00" func Append(logfile Logfile, compression bool, round float64, measuredValues []types.MeasuredValue) error { if round != 0 { for _, measuredValue := range measuredValues { measuredValue.SetValue(math.Round(measuredValue.GetValue()/round) * round) } } allMeasuredValues, err := logfile.Read() if err != nil { return err } allMeasuredValues = append(allMeasuredValues, measuredValues...) if compression { allMeasuredValues = Compression(allMeasuredValues) } err = logfile.Write(allMeasuredValues) if err != nil { return err } return nil } // Compression the measured values. The system checks whether the measured values // of the same type correspond to those of the predecessor. If this is the case, // the current value is discarded and the validity date of the previous value is // set to that of the current value. This means that no information is lost. // Only the validity period of the measured value is increased. func Compression(measuredValues []types.MeasuredValue) []types.MeasuredValue { compressedMeasuredValues := make([]types.MeasuredValue, 0) lastMeasuredValuesBySensors := make(map[string]map[types.MeasuredValueType]types.MeasuredValue, 0) // Sort all measured values according to the start time of the validity date // in order to successfully implement the subsequent compression. sort.SliceStable(measuredValues, func(i int, j int) bool { return measuredValues[i].GetFromDate().Before(measuredValues[j].GetFromDate()) }) now := time.Now() for _, measuredValue := range measuredValues { if _, ok := lastMeasuredValuesBySensors[measuredValue.GetSensorID()]; !ok { lastMeasuredValuesBySensors[measuredValue.GetSensorID()] = make(map[types.MeasuredValueType]types.MeasuredValue, 0) } if _, ok := lastMeasuredValuesBySensors[measuredValue.GetSensorID()][measuredValue.GetMeasuredValueType()]; !ok { lastMeasuredValuesBySensors[measuredValue.GetSensorID()][measuredValue.GetMeasuredValueType()] = measuredValue continue } if lastMeasuredValuesBySensors[measuredValue.GetSensorID()][measuredValue.GetMeasuredValueType()].GetValue() == measuredValue.GetValue() { lastMeasuredValuesBySensors[measuredValue.GetSensorID()][measuredValue.GetMeasuredValueType()].SetTillDate(measuredValue.GetTillDate()) lastMeasuredValuesBySensors[measuredValue.GetSensorID()][measuredValue.GetMeasuredValueType()].SetUpdateDate(&now) } else if lastMeasuredValuesBySensors[measuredValue.GetSensorID()][measuredValue.GetMeasuredValueType()].GetValue() != measuredValue.GetValue() { compressedMeasuredValues = append(compressedMeasuredValues, lastMeasuredValuesBySensors[measuredValue.GetSensorID()][measuredValue.GetMeasuredValueType()]) delete(lastMeasuredValuesBySensors[measuredValue.GetSensorID()], measuredValue.GetMeasuredValueType()) lastMeasuredValuesBySensors[measuredValue.GetSensorID()][measuredValue.GetMeasuredValueType()] = measuredValue } } // Copy all remaining entries from the map into the cache array for _, lastMeasuredValuesBySensor := range lastMeasuredValuesBySensors { for _, measuredValueType := range types.MeasuredValueTypes { if measuredValue, ok := lastMeasuredValuesBySensor[measuredValueType]; ok { compressedMeasuredValues = append(compressedMeasuredValues, measuredValue) } } } // Sort all measured values again to include the measured values from the // cache. sort.SliceStable(compressedMeasuredValues, func(i int, j int) bool { return compressedMeasuredValues[i].GetFromDate().Before(compressedMeasuredValues[j].GetFromDate()) }) return compressedMeasuredValues } // 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(logfile string) Logfile { ext := filepath.Ext(logfile) switch ext { case ".csv": return &csvLogfile{ logfile: logfile, } case ".json": return &jsonLogfile{ logfile: logfile, } // case ".xml": // return &xmlLogfile{ // logfile: logfile, // } default: return &jsonLogfile{ logfile: logfile, } } } func writeCreationDate(measuredValues []types.MeasuredValue) { now := time.Now() for _, measuredValue := range measuredValues { if measuredValue.GetCreationDate() == nil { measuredValue.SetCreationDate(&now) } } }