From f6db6f9ce35e9063bdf36795c575a697da75e50c Mon Sep 17 00:00:00 2001 From: Markus Pesch Date: Wed, 19 Jun 2019 19:18:01 +0200 Subject: [PATCH] feat(pkg/logfile): support csv and xml logfile --- cmd/temperature/list.go | 4 +- cmd/temperature/read.go | 3 +- go.mod | 1 - go.sum | 2 - pkg/daemon/daemon.go | 20 ++-- pkg/logfile/csv.go | 178 +++++++++++++++++++++++++++++ pkg/logfile/errors.go | 11 ++ pkg/logfile/interface.go | 13 +++ pkg/logfile/json.go | 96 ++++++++++++++++ pkg/logfile/logfile.go | 176 ++++++++-------------------- pkg/logfile/logfile_test.go | 7 -- pkg/logfile/utils.go | 19 +++ pkg/logfile/xml.go | 96 ++++++++++++++++ pkg/sensor/{error.go => errors.go} | 0 pkg/types/device.go | 14 +-- pkg/types/humidity.go | 13 ++- pkg/types/led.go | 32 +++--- pkg/types/sensor.go | 20 ++-- pkg/types/temperature.go | 14 +-- 19 files changed, 523 insertions(+), 196 deletions(-) create mode 100644 pkg/logfile/csv.go create mode 100644 pkg/logfile/errors.go create mode 100644 pkg/logfile/interface.go create mode 100644 pkg/logfile/json.go delete mode 100644 pkg/logfile/logfile_test.go create mode 100644 pkg/logfile/utils.go create mode 100644 pkg/logfile/xml.go rename pkg/sensor/{error.go => errors.go} (100%) diff --git a/cmd/temperature/list.go b/cmd/temperature/list.go index d447fa7..8ef7031 100644 --- a/cmd/temperature/list.go +++ b/cmd/temperature/list.go @@ -23,7 +23,9 @@ var listTemperatureCmd = &cobra.Command{ log.Fatalln(err) } - temperatures, err := logfile.ReadTemperatures(cnf.Device.TemperatureLogfile) + temperatureLogfile := logfile.New(cnf.Device.TemperatureLogfile) + + temperatures, err := temperatureLogfile.ReadTemperatures() if err != nil { log.Fatalln(err) } diff --git a/cmd/temperature/read.go b/cmd/temperature/read.go index 86df075..20d347e 100644 --- a/cmd/temperature/read.go +++ b/cmd/temperature/read.go @@ -45,7 +45,8 @@ var readTemperatureCmd = &cobra.Command{ cli.PrintTemperatures(temperatures, cnf, os.Stdout) if logs { - err := logfile.WriteTemperatures(temperatures, cnf.Device.TemperatureLogfile, compression) + temperatureLogfile := logfile.New(cnf.Device.TemperatureLogfile) + err := logfile.AppendTemperatures(temperatureLogfile, compression, temperatures) if err != nil { log.Fatalln(err) } diff --git a/go.mod b/go.mod index 6c1e4a0..4702156 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,5 @@ require ( github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 // indirect github.com/stianeikeland/go-rpio v4.2.0+incompatible - github.com/yryz/ds18b20 v0.0.0-20180211073435-3cf383a40624 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/go.sum b/go.sum index dbf4b3f..2e7cc6b 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,6 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stianeikeland/go-rpio v4.2.0+incompatible h1:CUOlIxdJdT+H1obJPsmg8byu7jMSECLfAN9zynm5QGo= github.com/stianeikeland/go-rpio v4.2.0+incompatible/go.mod h1:Sh81rdJwD96E2wja2Gd7rrKM+XZ9LrwvN2w4IXrqLR8= -github.com/yryz/ds18b20 v0.0.0-20180211073435-3cf383a40624 h1:bePzgtpuLSl+F9aacwuaquuoOyKfMKuJORq2CvPPJK4= -github.com/yryz/ds18b20 v0.0.0-20180211073435-3cf383a40624/go.mod h1:MqFju5qeLDFh+S9PqxYT7TEla8xeW7bgGr/69q3oki0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= periph.io/x/periph v3.4.0+incompatible h1:5gzxE4ryPq52cdqSw0mErR6pyJK8cBF2qdUAcOWh0bo= diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index d02f121..741e223 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -23,6 +23,8 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress logger.Info("Use compression: %v", compression) logger.Info("Round values: %v", round) + temperatureLogfile := logfile.New(cnf.Device.TemperatureLogfile) + ticker := time.Tick(cleanCacheInterval) interrupt := make(chan os.Signal, 1) @@ -38,7 +40,7 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress // go sensor.ReadHumiditiesContinuously(cnf.GetHumiditySensors(config.ENABLED), humidityChannel, errorChannel) go sensor.ReadTemperaturesContinuously(childContext, cnf.GetTemperatureSensors(config.ENABLED), round, temperatureChannel, errorChannel) - temperatures := make([]*types.Temperature, 0) + temperatureCache := make([]*types.Temperature, 0) rgbLEDs := cnf.GetRGBLEDs(config.ENABLED) err := rgbled.Green(rgbLEDs) @@ -57,12 +59,14 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress logger.Error("Can not turn on blue info light: %v", err) } - err = logfile.WriteTemperatures(temperatures, cnf.Device.TemperatureLogfile, compression) + //temperaturesLogfile,_ := temperatureLogfile.ReadTemperatures() + + err = logfile.AppendTemperatures(temperatureLogfile, compression, temperatureCache) if err != nil { cancel() logger.Fatal("Can not save temperatures: %v", err) } - temperatures = make([]*types.Temperature, 0) + temperatureCache = make([]*types.Temperature, 0) err = rgbled.Green(rgbLEDs) if err != nil { @@ -70,7 +74,7 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress } case temperature, _ := <-temperatureChannel: - temperatures = append(temperatures, temperature) + temperatureCache = append(temperatureCache, temperature) case killSignal := <-interrupt: logger.Warn("Daemon was interruped by system signal %v\n", killSignal) @@ -83,13 +87,9 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress } logger.Warn("Save remaining temperature data from the cache") - if compression { - temperatures = logfile.CompressTemperature(temperatures) - } - - err = logfile.WriteTemperatures(temperatures, cnf.Device.TemperatureLogfile, compression) + err = logfile.AppendTemperatures(temperatureLogfile, compression, temperatureCache) if err != nil { - logger.Fatal("Can not save temperatures: %v", err) + logger.Fatal("%v", err) } return diff --git a/pkg/logfile/csv.go b/pkg/logfile/csv.go new file mode 100644 index 0000000..3cd6f78 --- /dev/null +++ b/pkg/logfile/csv.go @@ -0,0 +1,178 @@ +package logfile + +import ( + "encoding/csv" + "encoding/json" + "fmt" + "os" + "strconv" + "time" + + "github.com/go-flucky/flucky/pkg/types" +) + +type csvLogfile struct { + logfile string +} + +func (cl *csvLogfile) GetLogfile() string { + return cl.logfile +} + +func (cl *csvLogfile) ReadHumidities() ([]*types.Humidity, error) { + if _, err := os.Stat(cl.logfile); os.IsNotExist(err) { + return nil, fmt.Errorf("%v: %v", ErrLogfileNotFound, cl.logfile) + } + + humidities := make([]*types.Humidity, 0) + + f, err := os.Open(cl.logfile) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileOpen, cl.logfile) + } + defer f.Close() + + jsonDecoder := json.NewDecoder(f) + err = jsonDecoder.Decode(&humidities) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileDecode, err) + } + + return humidities, nil +} + +func (cl *csvLogfile) ReadTemperatures() ([]*types.Temperature, error) { + if _, err := os.Stat(cl.logfile); os.IsNotExist(err) { + return nil, fmt.Errorf("%v: %v", ErrLogfileNotFound, cl.logfile) + } + + temperatures := make([]*types.Temperature, 0) + + f, err := os.Open(cl.logfile) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileOpen, cl.logfile) + } + defer f.Close() + + r := csv.NewReader(f) + records, err := r.ReadAll() + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileDecode, cl.logfile) + } + + for _, record := range records { + times := make([]time.Time, 0) + for _, j := range []int{2, 3} { + time, err := time.Parse(timeFormat, record[j]) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrParseTime, record[j]) + } + times = append(times, time) + } + + temperatureValue, err := strconv.ParseFloat(record[1], 64) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrParseFloat, record[1]) + } + + temperature := &types.Temperature{ + TemperatureID: record[0], + TemperatureValue: temperatureValue, + TemperatureFromDate: times[0], + TemperatureTillDate: times[1], + SensorID: record[4], + } + + if len(record) == 6 && record[5] != "" { + temperatureCreationDate, err := time.Parse(timeFormat, record[5]) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrParseTime, record[5]) + } + + temperature.CreationDate = &temperatureCreationDate + } + + if len(record) == 7 && record[6] != "" { + temperatureUpdateDate, err := time.Parse(timeFormat, record[6]) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrParseTime, record[6]) + } + + temperature.UpdateDate = &temperatureUpdateDate + } + + temperatures = append(temperatures, temperature) + } + + return temperatures, nil +} + +func (cl *csvLogfile) WriteHumidities(humidities []*types.Humidity) error { + + f, err := os.Create(cl.logfile) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogileCreate, cl.logfile) + } + defer f.Close() + + w := csv.NewWriter(f) + + for _, humidity := range humidities { + w.Write([]string{ + fmt.Sprintf("%v", humidity.HumidityID), + fmt.Sprintf("%v", humidity.HumidityValue), + fmt.Sprintf("%v", humidity.HumidityFromDate.Format(timeFormat)), + fmt.Sprintf("%v", humidity.HumidityTillDate.Format(timeFormat)), + fmt.Sprintf("%v", humidity.SensorID), + fmt.Sprintf("%v", humidity.CreationDate.Format(timeFormat)), + fmt.Sprintf("%v", humidity.UpdateDate.Format(timeFormat)), + }) + } + + w.Flush() + + return nil +} + +func (cl *csvLogfile) WriteTemperatures(temperatures []*types.Temperature) error { + f, err := os.Create(cl.logfile) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogileCreate, cl.logfile) + } + defer f.Close() + + writeCreationDate(temperatures) + + w := csv.NewWriter(f) + + for _, temperature := range temperatures { + record := make([]string, 0) + + if temperature.UpdateDate != nil { + record = []string{ + fmt.Sprintf("%v", temperature.TemperatureID), + fmt.Sprintf("%v", temperature.TemperatureValue), + fmt.Sprintf("%v", temperature.TemperatureFromDate.Format(timeFormat)), + fmt.Sprintf("%v", temperature.TemperatureTillDate.Format(timeFormat)), + fmt.Sprintf("%v", temperature.SensorID), + fmt.Sprintf("%v", temperature.CreationDate.Format(timeFormat)), + fmt.Sprintf("%v", temperature.UpdateDate.Format(timeFormat)), + } + } else { + record = []string{ + fmt.Sprintf("%v", temperature.TemperatureID), + fmt.Sprintf("%v", temperature.TemperatureValue), + fmt.Sprintf("%v", temperature.TemperatureFromDate.Format(timeFormat)), + fmt.Sprintf("%v", temperature.TemperatureTillDate.Format(timeFormat)), + fmt.Sprintf("%v", temperature.SensorID), + fmt.Sprintf("%v", temperature.CreationDate.Format(timeFormat)), + } + } + + w.Write(record) + } + + w.Flush() + + return nil +} diff --git a/pkg/logfile/errors.go b/pkg/logfile/errors.go new file mode 100644 index 0000000..889ad01 --- /dev/null +++ b/pkg/logfile/errors.go @@ -0,0 +1,11 @@ +package logfile + +import "errors" + +var ErrLogfileNotFound = errors.New("Can not find logfile") +var ErrLogileCreate = errors.New("Can not create logfile") +var ErrLogfileDecode = errors.New("Can not decode from reader") +var ErrLogfileEncode = errors.New("Cano not encode from writer") +var ErrLogfileOpen = errors.New("Can not open logfile") +var ErrParseFloat = errors.New("Can not parse float") +var ErrParseTime = errors.New("Can not parse time") diff --git a/pkg/logfile/interface.go b/pkg/logfile/interface.go new file mode 100644 index 0000000..289fe67 --- /dev/null +++ b/pkg/logfile/interface.go @@ -0,0 +1,13 @@ +package logfile + +import ( + "github.com/go-flucky/flucky/pkg/types" +) + +type Logfile interface { + GetLogfile() string + ReadHumidities() ([]*types.Humidity, error) + ReadTemperatures() ([]*types.Temperature, error) + WriteHumidities(humidities []*types.Humidity) error + WriteTemperatures(temperatures []*types.Temperature) error +} diff --git a/pkg/logfile/json.go b/pkg/logfile/json.go new file mode 100644 index 0000000..b8687a0 --- /dev/null +++ b/pkg/logfile/json.go @@ -0,0 +1,96 @@ +package logfile + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/go-flucky/flucky/pkg/types" +) + +type jsonLogfile struct { + logfile string +} + +func (jl *jsonLogfile) GetLogfile() string { + return jl.logfile +} + +func (jl *jsonLogfile) ReadHumidities() ([]*types.Humidity, error) { + if _, err := os.Stat(jl.logfile); os.IsNotExist(err) { + return nil, fmt.Errorf("%v: %v", ErrLogfileNotFound, jl.logfile) + } + + humidities := make([]*types.Humidity, 0) + + f, err := os.Open(jl.logfile) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileOpen, jl.logfile) + } + defer f.Close() + + jsonDecoder := json.NewDecoder(f) + err = jsonDecoder.Decode(&humidities) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileDecode, err) + } + + return humidities, nil +} + +func (jl *jsonLogfile) ReadTemperatures() ([]*types.Temperature, error) { + if _, err := os.Stat(jl.logfile); os.IsNotExist(err) { + return nil, fmt.Errorf("%v: %v", ErrLogfileNotFound, jl.logfile) + } + + temperatures := make([]*types.Temperature, 0) + + f, err := os.Open(jl.logfile) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileOpen, jl.logfile) + } + defer f.Close() + + jsonDecoder := json.NewDecoder(f) + err = jsonDecoder.Decode(&temperatures) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileDecode, err) + } + + return temperatures, nil +} + +func (jl *jsonLogfile) WriteHumidities(humidities []*types.Humidity) error { + + f, err := os.Create(jl.logfile) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogileCreate, jl.logfile) + } + defer f.Close() + + jsonEncoder := json.NewEncoder(f) + jsonEncoder.SetIndent("", " ") + err = jsonEncoder.Encode(humidities) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogfileEncode, err) + } + return nil +} + +func (jl *jsonLogfile) WriteTemperatures(temperatures []*types.Temperature) error { + f, err := os.Create(jl.logfile) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogileCreate, jl.logfile) + } + defer f.Close() + + writeCreationDate(temperatures) + + jsonEncoder := json.NewEncoder(f) + jsonEncoder.SetIndent("", " ") + err = jsonEncoder.Encode(temperatures) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogfileEncode, err) + } + return nil +} diff --git a/pkg/logfile/logfile.go b/pkg/logfile/logfile.go index 30d5fe8..48ba69a 100644 --- a/pkg/logfile/logfile.go +++ b/pkg/logfile/logfile.go @@ -1,9 +1,6 @@ package logfile import ( - "encoding/json" - "fmt" - "io" "os" "path/filepath" "sort" @@ -12,6 +9,33 @@ import ( "github.com/go-flucky/flucky/pkg/types" ) +// AppendTemperatures with temperature values from a logfile. As additional option it's possible to compress the temperature data. +func AppendTemperatures(logfile Logfile, compression bool, temperatures []*types.Temperature) error { + + allTemperatures := make([]*types.Temperature, 0) + + if _, err := os.Stat(logfile.GetLogfile()); err == nil { + temperaturesFromLogfile, err := logfile.ReadTemperatures() + if err != nil { + return err + } + + allTemperatures = append(allTemperatures, temperaturesFromLogfile...) + } + + allTemperatures = append(allTemperatures, temperatures...) + + if compression { + allTemperatures = CompressTemperature(allTemperatures) + } + + err := logfile.WriteTemperatures(allTemperatures) + if err != nil { + return err + } + return nil +} + // 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 @@ -51,60 +75,33 @@ func CompressTemperature(temperatures []*types.Temperature) []*types.Temperature return compressedTemperatures } -// ReadTemperatures from a file and returns an array with temperatures -func ReadTemperatures(temperatureLogfile string) ([]*types.Temperature, error) { +// 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 { - if _, err := os.Stat(temperatureLogfile); os.IsNotExist(err) { - return nil, fmt.Errorf("Can not find temperature logfile %v", temperatureLogfile) - } + ext := filepath.Ext(logfile) - 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 -} - -// ReadTemperaturesChannel reads temperatures from a channel until it is closed -// and returns the temperature. -func ReadTemperaturesChannel(temperatureChannel <-chan *types.Temperature) []*types.Temperature { - temperatures := make([]*types.Temperature, 0) - for { - select { - case temperature, more := <-temperatureChannel: - if more { - temperatures = append(temperatures, temperature) - } - default: - return temperatures + 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, } } } -// ReadTemperaturesCustom from a custom reader and returns an array with -// temperatures -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 -} - // SplittTemperatures into multiple arrays. The Size can be defined by // temperatureSplitBy parameter. func SplittTemperatures(temperatures []*types.Temperature, templeratureSplitBy int) [][]*types.Temperature { @@ -127,80 +124,3 @@ func SortTemperatures(temperatures []*types.Temperature) { return temperatures[i].TemperatureFromDate.Before(temperatures[j].TemperatureFromDate) }) } - -// WriteTemperatures encode temperatures into json and write it into a file. -// Compression can be enabled over a bolean parameter -func WriteTemperatures(temperatures []*types.Temperature, temperatureLogfile string, compression bool) error { - - allTemperatures := make([]*types.Temperature, 0) - - if _, err := os.Stat(temperatureLogfile); os.IsNotExist(err) { - err := os.MkdirAll(filepath.Dir(temperatureLogfile), 0755) - if err != nil { - return fmt.Errorf("Can not create directory %v to write temperatures into the logfile", filepath.Dir(temperatureLogfile)) - } - - f, err := os.Create(temperatureLogfile) - if err != nil { - return fmt.Errorf("Can not create file %v: %v", temperatureLogfile, err) - } - defer f.Close() - - } else { - f, err := os.Open(temperatureLogfile) - if err != nil { - return fmt.Errorf("Can not open file %v: %v", temperatureLogfile, err) - } - defer f.Close() - - savedTemperatures, err := ReadTemperaturesCustom(f) - if err != nil { - return fmt.Errorf("Can not read temperatures from logfile %v: %v", temperatureLogfile, err) - } - - allTemperatures = append(allTemperatures, savedTemperatures...) - } - - f, err := os.Create(temperatureLogfile) - if err != nil { - return fmt.Errorf("Can not create file %v: %v", temperatureLogfile, err) - } - defer f.Close() - - allTemperatures = append(allTemperatures, temperatures...) - - err = WriteTemperaturesCustom(allTemperatures, f, compression) - if err != nil { - return fmt.Errorf("Can not write temperatures to logfile %v: %v", temperatureLogfile, err) - } - - return nil -} - -// WriteTemperaturesCustom encode temperatures into json and write it into -// custom writer. Compression can be enabled over a bolean parameter -func WriteTemperaturesCustom(temperatures []*types.Temperature, w io.Writer, compression bool) error { - - writeCreationDate(temperatures) - - if compression { - temperatures = CompressTemperature(temperatures) - } - - jsonEncoder := json.NewEncoder(w) - jsonEncoder.SetIndent("", " ") - err := jsonEncoder.Encode(temperatures) - if err != nil { - return fmt.Errorf("Can not encode temperatures: %v", err) - } - return nil -} - -func writeCreationDate(temperatures []*types.Temperature) { - now := time.Now() - for _, temperature := range temperatures { - if temperature.CreationDate == nil { - temperature.CreationDate = &now - } - } -} diff --git a/pkg/logfile/logfile_test.go b/pkg/logfile/logfile_test.go deleted file mode 100644 index 1bfce18..0000000 --- a/pkg/logfile/logfile_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package logfile_test - -import "testing" - -func TestLogfile(t *testing.T) { - -} diff --git a/pkg/logfile/utils.go b/pkg/logfile/utils.go new file mode 100644 index 0000000..bbc5017 --- /dev/null +++ b/pkg/logfile/utils.go @@ -0,0 +1,19 @@ +package logfile + +import ( + "time" + + "github.com/go-flucky/flucky/pkg/types" +) + +//.999999999 -0700 MST +var timeFormat = "2006-01-02 15:04:05.999999999 -0700" + +func writeCreationDate(temperatures []*types.Temperature) { + now := time.Now() + for _, temperature := range temperatures { + if temperature.CreationDate == nil { + temperature.CreationDate = &now + } + } +} diff --git a/pkg/logfile/xml.go b/pkg/logfile/xml.go new file mode 100644 index 0000000..e702f05 --- /dev/null +++ b/pkg/logfile/xml.go @@ -0,0 +1,96 @@ +package logfile + +import ( + "encoding/xml" + "fmt" + "os" + + "github.com/go-flucky/flucky/pkg/types" +) + +type xmlLogfile struct { + logfile string +} + +func (xl *xmlLogfile) GetLogfile() string { + return xl.logfile +} + +func (xl *xmlLogfile) ReadHumidities() ([]*types.Humidity, error) { + if _, err := os.Stat(xl.logfile); os.IsNotExist(err) { + return nil, fmt.Errorf("%v: %v", ErrLogfileNotFound, xl.logfile) + } + + humidities := make([]*types.Humidity, 0) + + f, err := os.Open(xl.logfile) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileOpen, xl.logfile) + } + defer f.Close() + + xmlDecoder := xml.NewDecoder(f) + err = xmlDecoder.Decode(&humidities) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileDecode, err) + } + + return humidities, nil +} + +func (xl *xmlLogfile) ReadTemperatures() ([]*types.Temperature, error) { + if _, err := os.Stat(xl.logfile); os.IsNotExist(err) { + return nil, fmt.Errorf("%v: %v", ErrLogfileNotFound, xl.logfile) + } + + temperatures := make([]*types.Temperature, 0) + + f, err := os.Open(xl.logfile) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileOpen, xl.logfile) + } + defer f.Close() + + xmlDecoder := xml.NewDecoder(f) + err = xmlDecoder.Decode(&temperatures) + if err != nil { + return nil, fmt.Errorf("%v: %v", ErrLogfileDecode, err) + } + + return temperatures, nil +} + +func (xl *xmlLogfile) WriteHumidities(humidities []*types.Humidity) error { + + f, err := os.Create(xl.logfile) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogileCreate, xl.logfile) + } + defer f.Close() + + xmlEncoder := xml.NewEncoder(f) + xmlEncoder.Indent("", " ") + err = xmlEncoder.Encode(humidities) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogfileEncode, err) + } + return nil +} + +func (xl *xmlLogfile) WriteTemperatures(temperatures []*types.Temperature) error { + f, err := os.Create(xl.logfile) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogileCreate, xl.logfile) + } + defer f.Close() + + writeCreationDate(temperatures) + + xmlEncoder := xml.NewEncoder(f) + xmlEncoder.Indent("", " ") + err = xmlEncoder.Encode(temperatures) + if err != nil { + return fmt.Errorf("%v: %v", ErrLogfileEncode, err) + } + return nil +} diff --git a/pkg/sensor/error.go b/pkg/sensor/errors.go similarity index 100% rename from pkg/sensor/error.go rename to pkg/sensor/errors.go diff --git a/pkg/types/device.go b/pkg/types/device.go index 924d66e..7f14066 100644 --- a/pkg/types/device.go +++ b/pkg/types/device.go @@ -4,11 +4,11 @@ import "time" // Device ... type Device struct { - DeviceID string `json:"device_id"` - DeviceName string `json:"device_name"` - DeviceLocation *string `json:"device_location"` - DeviceLastContact time.Time `json:"device_last_contact"` - HumidityLogfile string `json:"humidity_logfile"` - TemperatureLogfile string `json:"temperature_logfile"` - CreationDate time.Time `json:"creation_date"` + DeviceID string `json:"device_id" xml:"device_id"` + DeviceName string `json:"device_name" xml:"device_name"` + DeviceLocation *string `json:"device_location" xml:"device_location"` + DeviceLastContact time.Time `json:"device_last_contact" xml:"device_last_contact"` + HumidityLogfile string `json:"humidity_logfile" xml:"humidity_logfile"` + TemperatureLogfile string `json:"temperature_logfile" xml:"temperature_logfile"` + CreationDate time.Time `json:"creation_date" xml:"creation_date"` } diff --git a/pkg/types/humidity.go b/pkg/types/humidity.go index edab41d..51f03c9 100644 --- a/pkg/types/humidity.go +++ b/pkg/types/humidity.go @@ -4,10 +4,11 @@ import "time" // Humidity ... type Humidity struct { - HumidityID string `json:"humidity_id"` - HumidityValue float64 `json:"humidity_value,string"` - HumidityFromDate time.Time `json:"humidity_from_date"` - HumidityTillDate time.Time `json:"humidity_till_date"` - SensorID string `json:"sensor_id"` - CreationDate time.Time `json:"creation_date"` + HumidityID string `json:"humidity_id" xml:"humidity_id"` + HumidityValue float64 `json:"humidity_value,string" xml:"humidity_value"` + HumidityFromDate time.Time `json:"humidity_from_date" xml:"humidity_from_date"` + HumidityTillDate time.Time `json:"humidity_till_date" xml:"humidity_till_date"` + SensorID string `json:"sensor_id" xml:"sensor_id"` + CreationDate *time.Time `json:"creation_date" xml:"creation_date"` + UpdateDate *time.Time `json:"update_date" xml:"update_date"` } diff --git a/pkg/types/led.go b/pkg/types/led.go index 0268018..5405e40 100644 --- a/pkg/types/led.go +++ b/pkg/types/led.go @@ -6,25 +6,25 @@ import ( ) type LED struct { - LEDID string `json:"led_id"` - LEDName string `json:"led_name"` - LEDLocation string `json:"led_location"` - GPIONumber *GPIO `json:"gpio_number"` - LEDEnabled bool `json:"led_enabled"` - LEDColor *LEDColor `json:"led_color"` - DeviceID string `json:"device_id"` - CreationDate time.Time `json:"creation_date"` + LEDID string `json:"led_id" xml:"led_id"` + LEDName string `json:"led_name" xml:"led_name"` + LEDLocation string `json:"led_location" xml:"led_location"` + GPIONumber *GPIO `json:"gpio_number" xml:"gpio_number"` + LEDEnabled bool `json:"led_enabled" xml:"led_enabled"` + LEDColor *LEDColor `json:"led_color" xml:"led_color"` + DeviceID string `json:"device_id" xml:"device_id"` + CreationDate time.Time `json:"creation_date" xml:"creation_date"` } type RGBLED struct { - RGBLEDID string `json:"rgbled_id"` - RGBLEDName string `json:"rgbled_name"` - RGBLEDLocation string `json:"rgbled_location"` - BaseColorsToGPIO map[BaseColor]*GPIO `json:"color_to_gpio"` - ActionMapping map[LEDOption]LEDColor `json:"action_mapping"` - RGBLEDEnabled bool `json:"rgbled_enabled"` - DeviceID string `json:"device_id"` - CreationDate time.Time `json:"creation_date"` + RGBLEDID string `json:"rgbled_id" xml:"rgbled_id"` + RGBLEDName string `json:"rgbled_name" xml:"rgbled_name"` + RGBLEDLocation string `json:"rgbled_location" xml:"rgb_location"` + BaseColorsToGPIO map[BaseColor]*GPIO `json:"color_to_gpio" xml:"color_to_gpio"` + ActionMapping map[LEDOption]LEDColor `json:"action_mapping" xml:"action_mapping"` + RGBLEDEnabled bool `json:"rgbled_enabled" xml:"rgb_enabled"` + DeviceID string `json:"device_id" xml:"device_id"` + CreationDate time.Time `json:"creation_date" xml:"creation_date"` } type BaseColor string diff --git a/pkg/types/sensor.go b/pkg/types/sensor.go index 1e7e62d..809d792 100644 --- a/pkg/types/sensor.go +++ b/pkg/types/sensor.go @@ -9,16 +9,16 @@ import ( // Sensor ... type Sensor struct { - SensorID string `json:"sensor_id"` - SensorName string `json:"sensor_name"` - SensorLocation string `json:"sensor_location"` - WireID *string `json:"wire_id"` - GPIONumber *GPIO `json:"gpio_number"` - SensorModel SensorModel `json:"sensor_model"` - SensorEnabled bool `json:"sensor_enabled"` - SensorLastContact time.Time `json:"sensor_last_contact"` - DeviceID string `json:"device_id"` - CreationDate time.Time `json:"creation_date"` + SensorID string `json:"sensor_id" xml:"sensor_id"` + SensorName string `json:"sensor_name" xml:"sensor_name"` + SensorLocation string `json:"sensor_location" xml:"sensor_location"` + WireID *string `json:"wire_id" xml:"wire_id"` + GPIONumber *GPIO `json:"gpio_number" xml:"gpio_number"` + SensorModel SensorModel `json:"sensor_model" xml:"sensor_model"` + SensorEnabled bool `json:"sensor_enabled" xml:"sensor_enabled"` + SensorLastContact time.Time `json:"sensor_last_contact" xml:"sensor_last_contact"` + DeviceID string `json:"device_id" xml:"device_id"` + CreationDate time.Time `json:"creation_date" xml:"creation_date"` } // JSONDecoder decodes a json into a sensor diff --git a/pkg/types/temperature.go b/pkg/types/temperature.go index 47197c9..8714fc3 100644 --- a/pkg/types/temperature.go +++ b/pkg/types/temperature.go @@ -4,11 +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"` - UpdateDate *time.Time `json:"update_date"` + TemperatureID string `json:"temperature_id" xml:"temperature_id"` + TemperatureValue float64 `json:"temperature_value,string" xml:"temperature_value,string"` + TemperatureFromDate time.Time `json:"temperature_from_date" xml:"temperature_from_date"` + TemperatureTillDate time.Time `json:"temperature_till_date" xml:"temperature_till_date"` + SensorID string `json:"sensor_id" xml:"sensor_id"` + CreationDate *time.Time `json:"creation_date" xml:"creation_date"` + UpdateDate *time.Time `json:"update_date" xml:"update_date"` }