package daemon import ( "context" "fmt" "os" "os/signal" "sync" "syscall" "github.com/go-flucky/flucky/pkg/config" "github.com/go-flucky/flucky/pkg/sensor" "github.com/go-flucky/flucky/pkg/types" "github.com/volker-raschek/go-logger/pkg/logger" ) var ( flogger = logger.NewDefaultLogger(logger.LogLevelDebug) ) // Start the daemon func Start(cnf *config.Configuration, cacheSize uint, compression bool, round float64) error { storageEndpointURL, err := cnf.GetStorageEndpointURL() if err != nil { return err } cache := &cacheStore{ compression: compression, cache: make([]*types.MeasuredValue, 0), mux: new(sync.Mutex), round: round, URL: storageEndpointURL, } // Context parentCtx := context.Background() ctx, cancel := context.WithCancel(parentCtx) // channels debugChannel := make(chan string, 0) infoChannel := make(chan string, 0) warnChannel := make(chan string, 0) errorChannel := make(chan error, 0) fatalChannel := make(chan error, 1) interruptChannel := make(chan os.Signal, 1) signal.Notify(interruptChannel, os.Interrupt, os.Kill, syscall.SIGTERM) measuredValueChannel := make(chan *types.MeasuredValue, 0) // Info flogger.Debug("Use compression: %v", compression) flogger.Debug("Round values to: %v", round) // Init semaphoreChannel semaphoreChannels := make(map[string]chan struct{}) for _, sensor := range cnf.GetSensors(config.ENABLED) { semaphoreChannels[sensor.GetID()] = make(chan struct{}, 1) } // Start producers for _, s := range cnf.GetSensors(config.ENABLED) { // start go routine for each sensor go func(sensor sensor.Sensor) { // run forever for { select { case <-ctx.Done(): return case <-semaphoreChannels[sensor.GetID()]: measuredValues, err := sensor.Read() if err != nil { errorChannel <- err return } for _, measuredValue := range measuredValues { measuredValueChannel <- measuredValue } } } }(s) // start ticker for each sensor go func(sensor sensor.Sensor) { for { select { case <-ctx.Done(): return case <-sensor.GetTicker().C: semaphoreChannels[sensor.GetID()] <- struct{}{} } } }(s) } go func() { for { select { case <-ctx.Done(): debugChannel <- fmt.Sprintf("Stop consumer of measured values: Closed context: %v", ctx.Err().Error()) return case measuredValue := <-measuredValueChannel: cache.Add(measuredValue) debugChannel <- fmt.Sprintf("CacheStore ID: %v, Sensor ID: %v, Type: %v, Value: %v", cache.Size(), measuredValue.SensorID, measuredValue.ValueType, measuredValue.Value) if cache.Size() >= int(cacheSize) { debugChannel <- fmt.Sprint("Write cache into storage endpoint") err := cache.WriteToEndpoint() if err != nil { errorChannel <- err } } } } }() for { select { case debug, _ := <-debugChannel: flogger.Debug("%v", debug) case info, _ := <-infoChannel: flogger.Info("%v", info) case warn, _ := <-warnChannel: flogger.Warn("%v", warn) case err, _ := <-errorChannel: flogger.Error("%v", err) case fatal, _ := <-fatalChannel: flogger.Fatal("Received a fatal error: %v", fatal) case <-interruptChannel: flogger.Debug("Write %v cached values into storage endpoint", cache.Size()) cache.WriteToEndpoint() flogger.Debug("Close context") cancel() flogger.Debug("Close channels") close(debugChannel) close(infoChannel) close(warnChannel) close(errorChannel) close(interruptChannel) return nil } } } // func checkDeviceInDatabase(ctx context.Context, device *types.Device, database db.Database) { // _, err := database.SelectDeviceByID(ctx, device.ID) // if err != nil { // flogger.Debug("It's seems the current device is not registered in the database. Register the device now") // err2 := database.InsertDevices(ctx, []*types.Device{device}) // if err2 != nil { // flogger.Fatal("Can not register device into database: %v", err2) // } // flogger.Debug("Device successfully registered into the database") // return // } // flogger.Debug("Device already registered into the database") // } // func checkSensorsInDatabase(ctx context.Context, sensors []*types.Sensor, database db.Database) { // for _, sensor := range sensors { // _, err := database.SelectSensorByID(ctx, sensor.ID) // if err != nil { // flogger.Debug("It's seems the sensor %v is not registered in the database. Register the sensor now", sensor.Name) // err2 := database.InsertSensors(ctx, []*types.Sensor{sensor}) // if err2 != nil { // flogger.Fatal("Can not register sensor %v into database: %v", sensor.Name, err2) // } // flogger.Debug("Sensor %v successfully registered into the database", sensor.Name) // continue // } // flogger.Debug("Sensor %v is already registered into the database", sensor.Name) // } // }