134 lines
4.5 KiB
Go
134 lines
4.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|